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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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();