diff --git a/lib/arjdbc/jdbc/adapter.rb b/lib/arjdbc/jdbc/adapter.rb index 77eb10f81..0365c780a 100644 --- a/lib/arjdbc/jdbc/adapter.rb +++ b/lib/arjdbc/jdbc/adapter.rb @@ -930,5 +930,8 @@ def _string_to_timestamp(value) end end + module Jdbc + autoload :TypeCast, 'arjdbc/jdbc/type_cast' + end end end diff --git a/lib/arjdbc/jdbc/type_cast.rb b/lib/arjdbc/jdbc/type_cast.rb new file mode 100644 index 000000000..0722febf7 --- /dev/null +++ b/lib/arjdbc/jdbc/type_cast.rb @@ -0,0 +1,140 @@ +module ActiveRecord::ConnectionAdapters + module Jdbc + # Type casting methods taken from AR 4.1's Column class. + # @private Simply to quickly "hack-in" 4.2 compatibility. + module TypeCast + + TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set + FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set + + #module Format + ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/ + ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ + #end + + # Used to convert from BLOBs to Strings + def binary_to_string(value) + value + end + + def value_to_date(value) + if value.is_a?(String) + return nil if value.empty? + fast_string_to_date(value) || fallback_string_to_date(value) + elsif value.respond_to?(:to_date) + value.to_date + else + value + end + end + + def string_to_time(string) + return string unless string.is_a?(String) + return nil if string.empty? + + fast_string_to_time(string) || fallback_string_to_time(string) + end + + def string_to_dummy_time(string) + return string unless string.is_a?(String) + return nil if string.empty? + + dummy_time_string = "2000-01-01 #{string}" + + fast_string_to_time(dummy_time_string) || begin + time_hash = Date._parse(dummy_time_string) + return nil if time_hash[:hour].nil? + new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)) + end + end + + # convert something to a boolean + def value_to_boolean(value) + if value.is_a?(String) && value.empty? + nil + else + TRUE_VALUES.include?(value) + end + end + + # Used to convert values to integer. + # handle the case when an integer column is used to store boolean values + def value_to_integer(value) + case value + when TrueClass, FalseClass + value ? 1 : 0 + else + value.to_i rescue nil + end + end + + # convert something to a BigDecimal + def value_to_decimal(value) + # Using .class is faster than .is_a? and + # subclasses of BigDecimal will be handled + # in the else clause + if value.class == BigDecimal + value + elsif value.respond_to?(:to_d) + value.to_d + else + value.to_s.to_d + end + end + + protected + # '0.123456' -> 123456 + # '1.123456' -> 123456 + def microseconds(time) + time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0 + end + + def new_date(year, mon, mday) + if year && year != 0 + Date.new(year, mon, mday) rescue nil + end + end + + def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) + # Treat 0000-00-00 00:00:00 as nil. + return nil if year.nil? || (year == 0 && mon == 0 && mday == 0) + + if offset + time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil + return nil unless time + + time -= offset + Base.default_timezone == :utc ? time : time.getlocal + else + Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil + end + end + + def fast_string_to_date(string) + if string =~ ISO_DATE + new_date $1.to_i, $2.to_i, $3.to_i + end + end + + # Doesn't handle time zones. + def fast_string_to_time(string) + if string =~ ISO_DATETIME + microsec = ($7.to_r * 1_000_000).to_i + new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec + end + end + + def fallback_string_to_date(string) + new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday)) + end + + def fallback_string_to_time(string) + time_hash = Date._parse(string) + time_hash[:sec_fraction] = microseconds(time_hash) + + new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)) + end + + end + end +end diff --git a/lib/arjdbc/postgresql/column.rb b/lib/arjdbc/postgresql/column.rb index 1d5f99440..7e51cd82b 100644 --- a/lib/arjdbc/postgresql/column.rb +++ b/lib/arjdbc/postgresql/column.rb @@ -28,6 +28,7 @@ class << base include ActiveRecord::ConnectionAdapters::PostgreSQL::ArrayParser end if AR4_COMPAT + include ActiveRecord::ConnectionAdapters::Jdbc::TypeCast if AR42_COMPAT include Cast end