3636import java .sql .Timestamp ;
3737import java .sql .Types ;
3838import java .util .List ;
39+ import java .util .Map ;
3940import java .util .UUID ;
4041
4142import org .jruby .Ruby ;
4243import org .jruby .RubyArray ;
4344import org .jruby .RubyBoolean ;
4445import org .jruby .RubyClass ;
4546import org .jruby .RubyFloat ;
47+ import org .jruby .RubyHash ;
4648import org .jruby .RubyIO ;
4749import org .jruby .RubyString ;
4850import org .jruby .anno .JRubyMethod ;
@@ -388,13 +390,23 @@ protected String resolveArrayBaseTypeName(final ThreadContext context,
388390 return sqlType ;
389391 }
390392
393+ private static final int HSTORE_TYPE = 100000 + 1111 ;
394+
391395 @ Override
392396 protected int jdbcTypeFor (final ThreadContext context , final Ruby runtime ,
393397 final IRubyObject column , final Object value ) throws SQLException {
394398 // NOTE: likely wrong but native adapters handles this thus we should
395399 // too - used from #table_exists? `binds << [ nil, schema ] if schema`
396400 if ( column == null || column .isNil () ) return Types .VARCHAR ; // assume type == :string
397- return super .jdbcTypeFor (context , runtime , column , value );
401+ final int type = super .jdbcTypeFor (context , runtime , column , value );
402+ /*
403+ if ( type == Types.OTHER ) {
404+ final IRubyObject columnType = column.callMethod(context, "type");
405+ if ( "hstore" == (Object) columnType.asJavaString() ) {
406+ return HSTORE_TYPE;
407+ }
408+ } */
409+ return type ;
398410 }
399411
400412 /**
@@ -467,6 +479,7 @@ protected IRubyObject arrayToRuby(final ThreadContext context,
467479 protected IRubyObject objectToRuby (final ThreadContext context ,
468480 final Ruby runtime , final ResultSet resultSet , final int column )
469481 throws SQLException {
482+
470483 final Object object = resultSet .getObject (column );
471484
472485 if ( object == null && resultSet .wasNull () ) return runtime .getNil ();
@@ -485,6 +498,16 @@ protected IRubyObject objectToRuby(final ThreadContext context,
485498 return runtime .newString ( object .toString () );
486499 }
487500
501+ if ( object instanceof Map ) { // hstore
502+ if ( rawHstoreType ) {
503+ return runtime .newString ( resultSet .getString (column ) );
504+ }
505+ // by default we avoid double parsing by driver and than column :
506+ final RubyHash rubyObject = RubyHash .newHash (runtime );
507+ rubyObject .putAll ((Map ) object ); // converts keys/values to ruby
508+ return rubyObject ;
509+ }
510+
488511 return JavaUtil .convertJavaToRuby (runtime , object );
489512 }
490513
@@ -529,6 +552,24 @@ private String formatInterval(final Object object) {
529552 return str .toString ();
530553 }
531554
555+ protected static boolean rawHstoreType = Boolean .getBoolean ("arjdbc.postgresql.hstore.raw" );
556+
557+ @ JRubyMethod (name = "raw_hstore_type?" )
558+ public static IRubyObject useRawHstoreType (final ThreadContext context , final IRubyObject self ) {
559+ return context .getRuntime ().newBoolean (rawHstoreType );
560+ }
561+
562+ @ JRubyMethod (name = "raw_hstore_type=" )
563+ public static IRubyObject setRawHstoreType (final IRubyObject self , final IRubyObject value ) {
564+ if ( value instanceof RubyBoolean ) {
565+ rawHstoreType = ((RubyBoolean ) value ).isTrue ();
566+ }
567+ else {
568+ rawHstoreType = ! value .isNil ();
569+ }
570+ return value ;
571+ }
572+
532573 // whether to use "raw" interval values off by default - due native adapter compatibilty :
533574 // RAW values :
534575 // - 2 years 0 mons 0 days 0 hours 3 mins 0.00 secs
@@ -549,7 +590,7 @@ public static IRubyObject setRawIntervalType(final IRubyObject self, final IRuby
549590 rawIntervalType = ((RubyBoolean ) value ).isTrue ();
550591 }
551592 else {
552- rawIntervalType = value .isNil ();
593+ rawIntervalType = ! value .isNil ();
553594 }
554595 return value ;
555596 }
0 commit comments