Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Make jdbc/postgres adapter convert array type elements (e.g. date[] a…

…rrays are returned as arrays of Date instances)

Before, the conversion proc would recursively map the array so that
all subarrays where turned into ruby arrays.  However, it would
not do any value conversion, so date[] arrays would be returned
as ruby arrays with Java::JavaSQL::Date instances.  Refactor this
to use a separate converter instance per array column, that uses
the standard scalar type conversion to convert elements of the
array.

Refactor specs so that some basic tests of this feature are run
on jdbc.  Unfortunately, it looks like not all types are handled,
so change the tests so that only the cases the jdbc/postgres
adapter currently handles are run on jdbc/postgres.

This is not backwards compatible if you were relying on the
jdbc/postgres array typecasting internals.
  • Loading branch information...
commit 938299f1cbda55562a1f80eb0ba4b7f0009f6b1f 1 parent 4d5988a
@jeremyevans authored
View
2  CHANGELOG
@@ -1,5 +1,7 @@
=== HEAD
+* Make jdbc/postgres adapter convert array type elements (e.g. date[] arrays are returned as arrays of Date instances) (jeremyevans)
+
* Make the pg_inet extension handle inet[]/cidr[]/macaddr[] types when used with the pg_array extension (jeremyevans)
* Make the pg_json extension handle json[] type when used with the pg_array extension (jeremyevans)
View
34 lib/sequel/adapters/jdbc/postgresql.rb
@@ -45,24 +45,43 @@ class Dataset < JDBC::Dataset
APOS = Dataset::APOS
class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
- # Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays
- def pg_array(v)
- _pg_array(v.array)
- end
-
# Convert Java::OrgPostgresqlUtil::PGobject to ruby strings
def pg_object(v)
v.to_string
end
+ end
+
+ # Handle conversions of PostgreSQL array instances
+ class PGArrayConverter
+ # Set the method that will return the correct conversion
+ # proc for elements of this array.
+ def initialize(meth)
+ @conversion_proc_method = meth
+ @conversion_proc = nil
+ end
+
+ # Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays
+ def call(v)
+ _pg_array(v.array)
+ end
private
# Handle multi-dimensional Java arrays by recursively mapping them
- # to ruby arrays.
+ # to ruby arrays of ruby values.
def _pg_array(v)
v.to_ary.map do |i|
if i.respond_to?(:to_ary)
_pg_array(i)
+ elsif i
+ if @conversion_proc.nil?
+ @conversion_proc = @conversion_proc_method.call(i)
+ end
+ if @conversion_proc
+ @conversion_proc.call(i)
+ else
+ i
+ end
else
i
end
@@ -70,7 +89,6 @@ def _pg_array(v)
end
end
- PG_ARRAY_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_array)
PG_OBJECT_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_object)
# Add the shared PostgreSQL prepared statement methods
@@ -88,7 +106,7 @@ def prepare(*args)
def convert_type_proc(v)
case v
when Java::OrgPostgresqlJdbc4::Jdbc4Array
- PG_ARRAY_METHOD
+ PGArrayConverter.new(method(:convert_type_proc))
when Java::OrgPostgresqlUtil::PGobject
PG_OBJECT_METHOD
else
View
79 spec/adapters/postgres_spec.rb
@@ -1354,6 +1354,7 @@ def @ds.check_return
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
@ds = @db[:items]
@native = POSTGRES_DB.adapter_scheme == :postgres
+ @jdbc = POSTGRES_DB.adapter_scheme == :jdbc
@tp = lambda{@db.schema(:items).map{|a| a.last[:type]}}
end
after do
@@ -1371,19 +1372,26 @@ def @ds.check_return
@tp.call.should == [:integer_array, :integer_array, :bigint_array, :float_array, :float_array]
@ds.insert([1].pg_array(:int2), [nil, 2].pg_array(:int4), [3, nil].pg_array(:int8), [4, nil, 4.5].pg_array(:real), [5, nil, 5.5].pg_array("double precision"))
@ds.count.should == 1
- if @native
- rs = @ds.all
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@ds.insert(rs.first)
@ds.all.should == rs
+ end
- @ds.delete
- @ds.insert([[1], [2]].pg_array(:int2), [[nil, 2], [3, 4]].pg_array(:int4), [[3, nil], [nil, nil]].pg_array(:int8), [[4, nil], [nil, 4.5]].pg_array(:real), [[5, nil], [nil, 5.5]].pg_array("double precision"))
- rs = @ds.all
+ @ds.delete
+ @ds.insert([[1], [2]].pg_array(:int2), [[nil, 2], [3, 4]].pg_array(:int4), [[3, nil], [nil, nil]].pg_array(:int8), [[4, nil], [nil, 4.5]].pg_array(:real), [[5, nil], [nil, 5.5]].pg_array("double precision"))
+
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@@ -1399,19 +1407,25 @@ def @ds.check_return
@tp.call.should == [:decimal_array]
@ds.insert([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')].pg_array(:numeric))
@ds.count.should == 1
- if @native
- rs = @ds.all
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@ds.insert(rs.first)
@ds.all.should == rs
+ end
- @ds.delete
- @ds.insert([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]].pg_array(:numeric))
- rs = @ds.all
+ @ds.delete
+ @ds.insert([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]].pg_array(:numeric))
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@@ -1429,19 +1443,25 @@ def @ds.check_return
@tp.call.should == [:string_array, :string_array, :string_array]
@ds.insert(['a', nil, 'NULL', 'b"\'c'].pg_array('char(4)'), ['a', nil, 'NULL', 'b"\'c'].pg_array(:varchar), ['a', nil, 'NULL', 'b"\'c'].pg_array(:text))
@ds.count.should == 1
- if @native
- rs = @ds.all
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c'], :t=>['a', nil, 'NULL', 'b"\'c']}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@ds.insert(rs.first)
@ds.all.should == rs
+ end
- @ds.delete
- @ds.insert([[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array('char(4)'), [[['a'], ['']], [['NULL'], ['b"\'c']]].pg_array(:varchar), [[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array(:text))
- rs = @ds.all
+ @ds.delete
+ @ds.insert([[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array('char(4)'), [[['a'], ['']], [['NULL'], ['b"\'c']]].pg_array(:varchar), [[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array(:text))
+ rs = @ds.all
+ if @jdbc || @native
rs.should == [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
+ end
+ if @native
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
@@ -1453,25 +1473,42 @@ def @ds.check_return
specify 'insert and retrieve arrays of other types' do
@db.create_table!(:items) do
column :b, 'bool[]'
- column :ba, 'bytea[]'
column :d, 'date[]'
column :t, 'time[]'
- column :tz, 'timetz[]'
column :ts, 'timestamp[]'
column :tstz, 'timestamptz[]'
- column :o, 'oid[]'
end
- @tp.call.should == [:boolean_array, :blob_array, :date_array, :time_array, :time_timezone_array, :datetime_array, :datetime_timezone_array, :integer_array]
+ @tp.call.should == [:boolean_array, :date_array, :time_array, :datetime_array, :datetime_timezone_array]
d = Date.today
t = Sequel::SQLTime.create(10, 20, 30)
ts = Time.local(2011, 1, 2, 3, 4, 5)
- @ds.insert([true, false].pg_array(:bool), [Sequel.blob("a\0"), nil].pg_array(:bytea), [d, nil].pg_array(:date), [t, nil].pg_array(:time), [t, nil].pg_array(:timetz), [ts, nil].pg_array(:timestamp), [ts, nil].pg_array(:timestamptz), [1, 2, 3].pg_array(:oid))
+ @ds.insert([true, false].pg_array(:bool), [d, nil].pg_array(:date), [t, nil].pg_array(:time), [ts, nil].pg_array(:timestamp), [ts, nil].pg_array(:timestamptz))
+ @ds.count.should == 1
+ rs = @ds.all
+ if @jdbc || @native
+ rs.should == [{:b=>[true, false], :d=>[d, nil], :t=>[t, nil], :ts=>[ts, nil], :tstz=>[ts, nil]}]
+ end
+ if @native
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
+ @ds.delete
+ @ds.insert(rs.first)
+ @ds.all.should == rs
+ end
+
+ @db.create_table!(:items) do
+ column :ba, 'bytea[]'
+ column :tz, 'timetz[]'
+ column :o, 'oid[]'
+ end
+ @tp.call.should == [:blob_array, :time_timezone_array, :integer_array]
+ @ds.insert( [Sequel.blob("a\0"), nil].pg_array(:bytea), [t, nil].pg_array(:timetz), [1, 2, 3].pg_array(:oid))
@ds.count.should == 1
if @native
rs = @ds.all
- rs.should == [{:b=>[true, false], :ba=>[Sequel.blob("a\0"), nil], :d=>[d, nil], :t=>[t, nil], :tz=>[t, nil], :ts=>[ts, nil], :tstz=>[ts, nil], :o=>[1, 2, 3]}]
+ rs.should == [{:ba=>[Sequel.blob("a\0"), nil], :tz=>[t, nil], :o=>[1, 2, 3]}]
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
@ds.delete
Please sign in to comment.
Something went wrong with that request. Please try again.