activerecord-jdbc-adapter 60.0-java → 61.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module ArJdbc
2
- VERSION = '60.0'
2
+ VERSION = '61.0'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests)
2
2
 
3
- TOMCAT_MAVEN_REPO = 'http://repo2.maven.org/maven2/org/apache/tomcat'
3
+ TOMCAT_MAVEN_REPO = 'https://repo1.maven.org/maven2/org/apache/tomcat'
4
4
  TOMCAT_VERSION = '7.0.54'
5
5
 
6
6
  DOWNLOAD_DIR = File.expand_path('../test/jars', File.dirname(__FILE__))
@@ -48,4 +48,4 @@ namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests)
48
48
  rm jar_path if File.exist?(jar_path)
49
49
  end
50
50
 
51
- end
51
+ end
@@ -51,7 +51,7 @@ namespace :rails do
51
51
  ruby_opts_string += " -C \"#{ar_path}\""
52
52
  ruby_opts_string += " -rbundler/setup"
53
53
  ruby_opts_string += " -rminitest -rminitest/excludes" unless ENV['NO_EXCLUDES'].eql?('true')
54
- file_list = ENV["TEST"] ? FileList[ ENV["TEST"] ] : test_files_finder.call
54
+ file_list = ENV["TEST"] ? FileList[ ENV["TEST"].split(',') ] : test_files_finder.call
55
55
  file_list_string = file_list.map { |fn| "\"#{fn}\"" }.join(' ')
56
56
  # test_loader_code = "-e \"ARGV.each{|f| require f}\"" # :direct
57
57
  option_list = ( ENV["TESTOPTS"] || ENV["TESTOPT"] || ENV["TEST_OPTS"] || '' )
@@ -86,6 +86,7 @@ import org.jruby.anno.JRubyMethod;
86
86
  import org.jruby.exceptions.RaiseException;
87
87
  import org.jruby.ext.bigdecimal.RubyBigDecimal;
88
88
  import org.jruby.ext.date.RubyDate;
89
+ import org.jruby.ext.date.RubyDateTime;
89
90
  import org.jruby.javasupport.JavaEmbedUtils;
90
91
  import org.jruby.javasupport.JavaUtil;
91
92
  import org.jruby.runtime.Block;
@@ -124,6 +125,7 @@ public class RubyJdbcConnection extends RubyObject {
124
125
  private IRubyObject config;
125
126
  private IRubyObject adapter; // the AbstractAdapter instance we belong to
126
127
  private volatile boolean connected = true;
128
+ private RubyClass attributeClass;
127
129
 
128
130
  private boolean lazy = false; // final once set on initialize
129
131
  private boolean jndi; // final once set on initialize
@@ -132,6 +134,7 @@ public class RubyJdbcConnection extends RubyObject {
132
134
 
133
135
  protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
134
136
  super(runtime, metaClass);
137
+ attributeClass = runtime.getModule("ActiveModel").getClass("Attribute");
135
138
  }
136
139
 
137
140
  private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
@@ -359,7 +362,7 @@ public class RubyJdbcConnection extends RubyObject {
359
362
  if ( ! connection.getAutoCommit() ) {
360
363
  try {
361
364
  connection.commit();
362
- resetSavepoints(context); // if any
365
+ resetSavepoints(context, connection); // if any
363
366
  return context.runtime.newBoolean(true);
364
367
  }
365
368
  finally {
@@ -380,7 +383,7 @@ public class RubyJdbcConnection extends RubyObject {
380
383
  if ( ! connection.getAutoCommit() ) {
381
384
  try {
382
385
  connection.rollback();
383
- resetSavepoints(context); // if any
386
+ resetSavepoints(context, connection); // if any
384
387
  return context.tru;
385
388
  } finally {
386
389
  connection.setAutoCommit(true);
@@ -516,7 +519,7 @@ public class RubyJdbcConnection extends RubyObject {
516
519
  return null;
517
520
  }
518
521
 
519
- protected boolean resetSavepoints(final ThreadContext context) {
522
+ protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
520
523
  if ( hasInternalVariable("savepoints") ) {
521
524
  removeInternalVariable("savepoints");
522
525
  return true;
@@ -610,11 +613,7 @@ public class RubyJdbcConnection extends RubyObject {
610
613
  return convertJavaToRuby( connection.unwrap(Connection.class) );
611
614
  }
612
615
  }
613
- catch (AbstractMethodError e) {
614
- debugStackTrace(context, e);
615
- warn(context, "driver/pool connection does not support unwrapping: " + e);
616
- }
617
- catch (SQLException e) {
616
+ catch (AbstractMethodError | SQLException e) {
618
617
  debugStackTrace(context, e);
619
618
  warn(context, "driver/pool connection does not support unwrapping: " + e);
620
619
  }
@@ -676,7 +675,10 @@ public class RubyJdbcConnection extends RubyObject {
676
675
 
677
676
  private void connectImpl(final boolean forceConnection) throws SQLException {
678
677
  setConnection( forceConnection ? newConnection() : null );
679
- if ( forceConnection ) configureConnection();
678
+ if (forceConnection) {
679
+ if (getConnectionImpl() == null) throw new SQLException("Didn't get a connection. Wrong URL?");
680
+ configureConnection();
681
+ }
680
682
  }
681
683
 
682
684
  @JRubyMethod(name = "read_only?")
@@ -832,24 +834,45 @@ public class RubyJdbcConnection extends RubyObject {
832
834
  return mapQueryResult(context, connection, resultSet);
833
835
  }
834
836
 
837
+ private static String[] createStatementPk(IRubyObject pk) {
838
+ String[] statementPk;
839
+ if (pk instanceof RubyArray) {
840
+ RubyArray ary = (RubyArray) pk;
841
+ int size = ary.size();
842
+ statementPk = new String[size];
843
+ for (int i = 0; i < size; i++) {
844
+ statementPk[i] = sqlString(ary.eltInternal(i));
845
+ }
846
+ } else {
847
+ statementPk = new String[] { sqlString(pk) };
848
+ }
849
+ return statementPk;
850
+ }
851
+
835
852
  /**
836
853
  * Executes an INSERT SQL statement
837
854
  * @param context
838
855
  * @param sql
856
+ * @param pk Rails PK
839
857
  * @return ActiveRecord::Result
840
858
  * @throws SQLException
841
859
  */
842
- @JRubyMethod(name = "execute_insert", required = 1)
843
- public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
860
+ @JRubyMethod(name = "execute_insert_pk", required = 2)
861
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
844
862
  return withConnection(context, connection -> {
845
863
  Statement statement = null;
846
864
  final String query = sqlString(sql);
847
865
  try {
848
866
 
849
867
  statement = createStatement(context, connection);
850
- statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
851
- return mapGeneratedKeys(context, connection, statement);
852
868
 
869
+ if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
870
+ statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
871
+ } else {
872
+ statement.executeUpdate(query, createStatementPk(pk));
873
+ }
874
+
875
+ return mapGeneratedKeys(context, connection, statement);
853
876
  } catch (final SQLException e) {
854
877
  debugErrorSQL(context, query);
855
878
  throw e;
@@ -859,26 +882,37 @@ public class RubyJdbcConnection extends RubyObject {
859
882
  });
860
883
  }
861
884
 
885
+ @Deprecated
886
+ @JRubyMethod(name = "execute_insert", required = 1)
887
+ public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
888
+ return execute_insert_pk(context, sql, context.nil);
889
+ }
890
+
862
891
  /**
863
892
  * Executes an INSERT SQL statement using a prepared statement
864
893
  * @param context
865
894
  * @param sql
866
895
  * @param binds RubyArray of values to be bound to the query
896
+ * @param pk Rails PK
867
897
  * @return ActiveRecord::Result
868
898
  * @throws SQLException
869
899
  */
870
- @JRubyMethod(name = "execute_insert", required = 2)
871
- public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql, final IRubyObject binds) {
900
+ @JRubyMethod(name = "execute_insert_pk", required = 3)
901
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
902
+ final IRubyObject pk) {
872
903
  return withConnection(context, connection -> {
873
904
  PreparedStatement statement = null;
874
905
  final String query = sqlString(sql);
875
906
  try {
907
+ if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
908
+ statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
909
+ } else {
910
+ statement = connection.prepareStatement(query, createStatementPk(pk));
911
+ }
876
912
 
877
- statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
878
913
  setStatementParameters(context, connection, statement, (RubyArray) binds);
879
914
  statement.executeUpdate();
880
915
  return mapGeneratedKeys(context, connection, statement);
881
-
882
916
  } catch (final SQLException e) {
883
917
  debugErrorSQL(context, query);
884
918
  throw e;
@@ -888,6 +922,12 @@ public class RubyJdbcConnection extends RubyObject {
888
922
  });
889
923
  }
890
924
 
925
+ @Deprecated
926
+ @JRubyMethod(name = "execute_insert", required = 2)
927
+ public IRubyObject execute_insert(final ThreadContext context, final IRubyObject binds, final IRubyObject sql) {
928
+ return execute_insert_pk(context, sql, binds, context.nil);
929
+ }
930
+
891
931
  /**
892
932
  * Executes an UPDATE (DELETE) SQL statement
893
933
  * @param context
@@ -967,12 +1007,12 @@ public class RubyJdbcConnection extends RubyObject {
967
1007
  binds = null;
968
1008
  } else { // (sql, binds)
969
1009
  maxRows = 0;
970
- binds = (RubyArray) TypeConverter.checkArrayType(args[1]);
1010
+ binds = (RubyArray) TypeConverter.checkArrayType(context, args[1]);
971
1011
  }
972
1012
  break;
973
1013
  case 3: // (sql, max_rows, binds)
974
1014
  maxRows = RubyNumeric.fix2int(args[1]);
975
- binds = (RubyArray) TypeConverter.checkArrayType(args[2]);
1015
+ binds = (RubyArray) TypeConverter.checkArrayType(context, args[2]);
976
1016
  break;
977
1017
  default: // (sql) 1-arg
978
1018
  maxRows = 0;
@@ -1061,6 +1101,28 @@ public class RubyJdbcConnection extends RubyObject {
1061
1101
  });
1062
1102
  }
1063
1103
 
1104
+ @JRubyMethod(required = 1)
1105
+ public IRubyObject get_first_value(final ThreadContext context, final IRubyObject sql) {
1106
+ return withConnection(context, connection -> {
1107
+ Statement statement = null;
1108
+ final String query = sqlString(sql);
1109
+ try {
1110
+ statement = createStatement(context, connection);
1111
+ statement.execute(query);
1112
+ ResultSet rs = statement.getResultSet();
1113
+ if (rs == null || !rs.next()) return context.nil;
1114
+
1115
+ return jdbcToRuby(context, context.getRuntime(), 1, rs.getMetaData().getColumnType(1), rs);
1116
+
1117
+ } catch (final SQLException e) {
1118
+ debugErrorSQL(context, query);
1119
+ throw e;
1120
+ } finally {
1121
+ close(statement);
1122
+ }
1123
+ });
1124
+ }
1125
+
1064
1126
  /**
1065
1127
  * Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
1066
1128
  * @param context which context this method is executing on.
@@ -2120,7 +2182,7 @@ public class RubyJdbcConnection extends RubyObject {
2120
2182
  return RubyString.newString(runtime, DateTimeUtils.dateToString(value));
2121
2183
  }
2122
2184
 
2123
- return DateTimeUtils.newDateAsTime(context, value, null).callMethod(context, "to_date");
2185
+ return DateTimeUtils.newDateAsTime(context, value, DateTimeZone.UTC).callMethod(context, "to_date");
2124
2186
  }
2125
2187
 
2126
2188
  protected IRubyObject timeToRuby(final ThreadContext context,
@@ -2357,9 +2419,16 @@ public class RubyJdbcConnection extends RubyObject {
2357
2419
  final Connection connection, final PreparedStatement statement,
2358
2420
  final int index, IRubyObject attribute) throws SQLException {
2359
2421
 
2360
- //debugMessage(context, attribute);
2361
- final int type = jdbcTypeForAttribute(context, attribute);
2362
- IRubyObject value = valueForDatabase(context, attribute);
2422
+ final IRubyObject value;
2423
+ final int type;
2424
+
2425
+ if (attributeClass.isInstance(attribute)) {
2426
+ type = jdbcTypeForAttribute(context, attribute);
2427
+ value = valueForDatabase(context, attribute);
2428
+ } else {
2429
+ type = jdbcTypeForPrimitiveAttribute(context, attribute);
2430
+ value = attribute;
2431
+ }
2363
2432
 
2364
2433
  // All the set methods were calling this first so save a method call in the nil case
2365
2434
  if ( value == context.nil ) {
@@ -2475,6 +2544,34 @@ public class RubyJdbcConnection extends RubyObject {
2475
2544
  return Types.OTHER; // -1 as well as 0 are used in Types
2476
2545
  }
2477
2546
 
2547
+ protected String internedTypeForPrimitive(final ThreadContext context, final IRubyObject value) throws SQLException {
2548
+ if (value instanceof RubyString) {
2549
+ return "string";
2550
+ }
2551
+ if (value instanceof RubyInteger) {
2552
+ return "integer";
2553
+ }
2554
+ if (value instanceof RubyNumeric) {
2555
+ return "float";
2556
+ }
2557
+ if (value instanceof RubyTime || value instanceof RubyDateTime) {
2558
+ return "timestamp";
2559
+ }
2560
+ if (value instanceof RubyDate) {
2561
+ return "date";
2562
+ }
2563
+ if (value instanceof RubyBoolean) {
2564
+ return "boolean";
2565
+ }
2566
+ return "string";
2567
+ }
2568
+
2569
+ protected Integer jdbcTypeForPrimitiveAttribute(final ThreadContext context,
2570
+ final IRubyObject attribute) throws SQLException {
2571
+ final String internedType = internedTypeForPrimitive(context, attribute);
2572
+ return jdbcTypeFor(internedType);
2573
+ }
2574
+
2478
2575
  protected Integer jdbcTypeFor(final String type) {
2479
2576
  return JDBC_TYPE_FOR.get(type);
2480
2577
  }
@@ -2486,7 +2583,9 @@ public class RubyJdbcConnection extends RubyObject {
2486
2583
  }
2487
2584
 
2488
2585
  protected static IRubyObject attributeSQLType(final ThreadContext context, final IRubyObject attribute) {
2489
- return attributeType(context, attribute).callMethod(context, "type");
2586
+ final IRubyObject type = attributeType(context, attribute);
2587
+ if (type != null) return type.callMethod(context, "type");
2588
+ return context.nil;
2490
2589
  }
2491
2590
 
2492
2591
  private final CachingCallSite value_site = new FunctionalCachingCallSite("value"); // AR::Attribute#value
@@ -2499,23 +2598,7 @@ public class RubyJdbcConnection extends RubyObject {
2499
2598
 
2500
2599
  final IRubyObject value = value_site.call(context, attribute, attribute);
2501
2600
 
2502
- if (value instanceof RubyInteger) {
2503
- return "integer";
2504
- }
2505
-
2506
- if (value instanceof RubyNumeric) {
2507
- return "float";
2508
- }
2509
-
2510
- if (value instanceof RubyTime) {
2511
- return "timestamp";
2512
- }
2513
-
2514
- if (value instanceof RubyBoolean) {
2515
- return "boolean";
2516
- }
2517
-
2518
- return "string";
2601
+ return internedTypeForPrimitive(context, value);
2519
2602
  }
2520
2603
 
2521
2604
  protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
@@ -200,6 +200,57 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
200
200
  });
201
201
  }
202
202
 
203
+ /**
204
+ * Executes an INSERT SQL statement
205
+ * @param context
206
+ * @param sql
207
+ * @param pk Rails PK
208
+ * @return ActiveRecord::Result
209
+ * @throws SQLException
210
+ */
211
+ @Override
212
+ @JRubyMethod(name = "execute_insert_pk", required = 2)
213
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
214
+
215
+ // MSSQL does not like composite primary keys here so chop it if there is more than one column
216
+ IRubyObject modifiedPk = pk;
217
+
218
+ if (pk instanceof RubyArray) {
219
+ RubyArray ary = (RubyArray) pk;
220
+ if (ary.size() > 0) {
221
+ modifiedPk = ary.eltInternal(0);
222
+ }
223
+ }
224
+
225
+ return super.execute_insert_pk(context, sql, modifiedPk);
226
+ }
227
+
228
+ /**
229
+ * Executes an INSERT SQL statement using a prepared statement
230
+ * @param context
231
+ * @param sql
232
+ * @param binds RubyArray of values to be bound to the query
233
+ * @param pk Rails PK
234
+ * @return ActiveRecord::Result
235
+ * @throws SQLException
236
+ */
237
+ @Override
238
+ @JRubyMethod(name = "execute_insert_pk", required = 3)
239
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
240
+ final IRubyObject pk) {
241
+ // MSSQL does not like composite primary keys here so chop it if there is more than one column
242
+ IRubyObject modifiedPk = pk;
243
+
244
+ if (pk instanceof RubyArray) {
245
+ RubyArray ary = (RubyArray) pk;
246
+ if (ary.size() > 0) {
247
+ modifiedPk = ary.eltInternal(0);
248
+ }
249
+ }
250
+
251
+ return super.execute_insert_pk(context, sql, binds, modifiedPk);
252
+ }
253
+
203
254
  @Override
204
255
  protected Integer jdbcTypeFor(final String type) {
205
256
 
@@ -36,26 +36,18 @@ import java.io.ByteArrayInputStream;
36
36
  import java.lang.StringBuilder;
37
37
  import java.lang.reflect.InvocationTargetException;
38
38
  import java.math.BigDecimal;
39
- import java.sql.Connection;
40
- import java.sql.DatabaseMetaData;
41
- import java.sql.PreparedStatement;
42
- import java.sql.ResultSet;
43
- import java.sql.ResultSetMetaData;
44
- import java.sql.SQLException;
45
- import java.sql.Timestamp;
46
- import java.sql.Types;
47
- import java.util.ArrayList;
48
- import java.util.Collections;
49
- import java.util.HashMap;
50
- import java.util.Map;
51
- import java.util.UUID;
39
+ import java.sql.*;
40
+ import java.sql.Date;
41
+ import java.util.*;
52
42
  import java.util.regex.Pattern;
53
43
  import java.util.regex.Matcher;
54
44
 
45
+ import org.joda.time.DateTime;
55
46
  import org.jruby.*;
56
47
  import org.jruby.anno.JRubyMethod;
57
48
  import org.jruby.exceptions.RaiseException;
58
49
  import org.jruby.ext.bigdecimal.RubyBigDecimal;
50
+ import org.jruby.ext.date.RubyDate;
59
51
  import org.jruby.javasupport.JavaUtil;
60
52
  import org.jruby.runtime.ObjectAllocator;
61
53
  import org.jruby.runtime.ThreadContext;
@@ -115,6 +107,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
115
107
 
116
108
  // Used to wipe trailing 0's from points (3.0, 5.6) -> (3, 5.6)
117
109
  private static final Pattern pointCleanerPattern = Pattern.compile("\\.0\\b");
110
+ private static final TimeZone TZ_DEFAULT = TimeZone.getDefault();
118
111
 
119
112
  private RubyClass resultClass;
120
113
  private RubyHash typeMap = null;
@@ -204,9 +197,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
204
197
  RubyClass arrayClass = oidArray(context);
205
198
  RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
206
199
  // The type or its delegate is an OID::Array
207
- if (arrayClass.isInstance(attributeType) ||
200
+ if (attributeType != null && (arrayClass.isInstance(attributeType) ||
208
201
  (attributeType.hasInstanceVariable("@delegate_dc_obj") &&
209
- arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
202
+ arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj"))))) {
210
203
  return "array";
211
204
  }
212
205
 
@@ -386,7 +379,20 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
386
379
  }
387
380
  }
388
381
 
389
- super.setDateParameter(context, connection, statement, index, value, attribute, type);
382
+ if ( ! "Date".equals(value.getMetaClass().getName()) && value.respondsTo("to_date") ) {
383
+ value = value.callMethod(context, "to_date");
384
+ }
385
+
386
+ if (value instanceof RubyDate) {
387
+ RubyDate rubyDate = (RubyDate) value;
388
+ DateTime dt = rubyDate.getDateTime();
389
+ // pgjdbc needs adjustment for default JVM timezone
390
+ statement.setDate(index, new Date(dt.getMillis() - TZ_DEFAULT.getOffset(dt.getMillis())));
391
+ return;
392
+ }
393
+
394
+ // NOTE: assuming Date#to_s does right ...
395
+ statement.setDate(index, Date.valueOf(value.toString()));
390
396
  }
391
397
 
392
398
  @Override
@@ -430,7 +436,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
430
436
  break;
431
437
 
432
438
  case "interval":
433
- statement.setObject(index, new PGInterval(value.toString()));
439
+ statement.setObject(index, stringToPGInterval(value.toString()));
434
440
  break;
435
441
 
436
442
  case "json":
@@ -487,6 +493,74 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
487
493
  }
488
494
  }
489
495
 
496
+ private int lookAhead(String value, int position, String find) {
497
+ char [] tokens = find.toCharArray();
498
+ int found = -1;
499
+
500
+ for ( int i = 0; i < tokens.length; i++ ) {
501
+ found = value.indexOf(tokens[i], position);
502
+ if ( found > 0 ) {
503
+ return found;
504
+ }
505
+ }
506
+ return found;
507
+ }
508
+
509
+ private Object stringToPGInterval(String value) throws SQLException {
510
+ if (!value.startsWith("P")) return new PGInterval(value);
511
+
512
+ PGInterval interval = new PGInterval();
513
+
514
+ /* this is copied from pgjdbc with fixes for Rails */
515
+ int number = 0;
516
+ String dateValue;
517
+ String timeValue = null;
518
+
519
+ int hasTime = value.indexOf('T');
520
+ if ( hasTime > 0 ) {
521
+ /* skip over the P */
522
+ dateValue = value.substring(1,hasTime);
523
+ timeValue = value.substring(hasTime + 1);
524
+ } else {
525
+ /* skip over the P */
526
+ dateValue = value.substring(1);
527
+ }
528
+
529
+ for ( int i = 0; i < dateValue.length(); i++ ) {
530
+ int lookAhead = lookAhead(dateValue, i, "YMD");
531
+ if (lookAhead > 0) {
532
+ char type = dateValue.charAt(lookAhead);
533
+ number = Integer.parseInt(dateValue.substring(i, lookAhead));
534
+ if (type == 'Y') {
535
+ interval.setYears(number);
536
+ } else if (type == 'M') {
537
+ interval.setMonths(number);
538
+ } else if (type == 'D') {
539
+ interval.setDays(number);
540
+ }
541
+ i = lookAhead;
542
+ }
543
+ }
544
+ if ( timeValue != null ) {
545
+ for (int i = 0; i < timeValue.length(); i++) {
546
+ int lookAhead = lookAhead(timeValue, i, "HMS");
547
+ if (lookAhead > 0) {
548
+ char type = timeValue.charAt(lookAhead);
549
+ String part = timeValue.substring(i, lookAhead);
550
+ if (timeValue.charAt(lookAhead) == 'H') {
551
+ interval.setHours(Integer.parseInt(part));
552
+ } else if (timeValue.charAt(lookAhead) == 'M') {
553
+ interval.setMinutes(Integer.parseInt(part));
554
+ } else if (timeValue.charAt(lookAhead) == 'S') {
555
+ interval.setSeconds(Double.parseDouble(part));
556
+ }
557
+ i = lookAhead;
558
+ }
559
+ }
560
+ }
561
+ return interval;
562
+ }
563
+
490
564
  protected IRubyObject jdbcToRuby(ThreadContext context, Ruby runtime, int column, int type, ResultSet resultSet) throws SQLException {
491
565
  return typeMap != null ?
492
566
  convertWithTypeMap(context, runtime, column, type, resultSet) :
@@ -868,34 +942,25 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
868
942
  private static String formatInterval(final Object object) {
869
943
  final PGInterval interval = (PGInterval) object;
870
944
  final StringBuilder str = new StringBuilder(32);
945
+ str.append("P");
871
946
 
872
947
  final int years = interval.getYears();
873
- if (years != 0) str.append(years).append(" years ");
948
+ if (years != 0) str.append(years).append("Y");
874
949
 
875
950
  final int months = interval.getMonths();
876
- if (months != 0) str.append(months).append(" months ");
951
+ if (months != 0) str.append(months).append("M");
877
952
 
878
953
  final int days = interval.getDays();
879
- if (days != 0) str.append(days).append(" days ");
954
+ if (days != 0) str.append(days).append("D");
880
955
 
881
956
  final int hours = interval.getHours();
882
957
  final int mins = interval.getMinutes();
883
- final int secs = (int) interval.getSeconds();
884
- if (hours != 0 || mins != 0 || secs != 0) { // xx:yy:zz if not all 00
885
- if (hours < 10) str.append('0');
886
-
887
- str.append(hours).append(':');
888
-
889
- if (mins < 10) str.append('0');
890
-
891
- str.append(mins).append(':');
892
-
893
- if (secs < 10) str.append('0');
894
-
895
- str.append(secs);
896
-
897
- } else if (str.length() > 1) {
898
- str.deleteCharAt(str.length() - 1); // " " at the end
958
+ final double secs = interval.getSeconds();
959
+ if (hours != 0 || mins != 0 || secs != 0) {
960
+ str.append("T");
961
+ if (hours != 0) str.append(hours).append("H");
962
+ if (mins != 0) str.append(mins).append("M");
963
+ if (secs != 0) str.append(secs).append("S");
899
964
  }
900
965
 
901
966
  return str.toString();