activerecord-jdbc-alt-adapter 52.4.0-java → 61.0.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.nvimlog +0 -0
  4. data/.travis.yml +63 -39
  5. data/Gemfile +11 -4
  6. data/README.md +55 -35
  7. data/Rakefile +1 -1
  8. data/Rakefile.jdbc +8 -1
  9. data/activerecord-jdbc-adapter.gemspec +6 -9
  10. data/activerecord-jdbc-alt-adapter.gemspec +9 -12
  11. data/lib/arel/visitors/postgresql_jdbc.rb +1 -1
  12. data/lib/arel/visitors/sqlserver.rb +49 -23
  13. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  14. data/lib/arjdbc/abstract/core.rb +17 -23
  15. data/lib/arjdbc/abstract/database_statements.rb +30 -2
  16. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  17. data/lib/arjdbc/abstract/transaction_support.rb +22 -7
  18. data/lib/arjdbc/db2/column.rb +0 -39
  19. data/lib/arjdbc/derby/adapter.rb +1 -20
  20. data/lib/arjdbc/firebird/adapter.rb +0 -21
  21. data/lib/arjdbc/h2/adapter.rb +0 -15
  22. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  23. data/lib/arjdbc/informix/adapter.rb +0 -23
  24. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  25. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  26. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  27. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  28. data/lib/arjdbc/jdbc/column.rb +2 -0
  29. data/lib/arjdbc/jdbc/connection.rb +2 -0
  30. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  31. data/lib/arjdbc/jdbc/error.rb +2 -0
  32. data/lib/arjdbc/jdbc/extension.rb +2 -0
  33. data/lib/arjdbc/jdbc/java.rb +3 -1
  34. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  35. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  36. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  37. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  38. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  39. data/lib/arjdbc/mssql.rb +3 -1
  40. data/lib/arjdbc/mssql/adapter.rb +114 -36
  41. data/lib/arjdbc/mssql/column.rb +19 -1
  42. data/lib/arjdbc/mssql/connection_methods.rb +10 -2
  43. data/lib/arjdbc/mssql/database_limits.rb +9 -0
  44. data/lib/arjdbc/mssql/database_statements.rb +44 -6
  45. data/lib/arjdbc/mssql/errors.rb +2 -0
  46. data/lib/arjdbc/mssql/explain_support.rb +3 -1
  47. data/lib/arjdbc/mssql/extensions/attribute_methods.rb +6 -2
  48. data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
  49. data/lib/arjdbc/mssql/quoting.rb +38 -0
  50. data/lib/arjdbc/mssql/schema_creation.rb +25 -3
  51. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  52. data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
  53. data/lib/arjdbc/mssql/schema_statements.rb +92 -22
  54. data/lib/arjdbc/mssql/transaction.rb +2 -0
  55. data/lib/arjdbc/mssql/types.rb +2 -0
  56. data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
  57. data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
  58. data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
  59. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
  60. data/lib/arjdbc/mssql/types/string_types.rb +2 -0
  61. data/lib/arjdbc/mssql/utils.rb +2 -0
  62. data/lib/arjdbc/mysql/adapter.rb +59 -21
  63. data/lib/arjdbc/mysql/connection_methods.rb +6 -1
  64. data/lib/arjdbc/postgresql/adapter.rb +257 -219
  65. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  66. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  67. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  68. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  69. data/lib/arjdbc/postgresql/column.rb +6 -4
  70. data/lib/arjdbc/postgresql/connection_methods.rb +1 -0
  71. data/lib/arjdbc/postgresql/name.rb +2 -0
  72. data/lib/arjdbc/postgresql/oid_types.rb +7 -4
  73. data/lib/arjdbc/sqlite3/adapter.rb +266 -221
  74. data/lib/arjdbc/sqlite3/connection_methods.rb +26 -4
  75. data/lib/arjdbc/tasks/databases.rake +21 -13
  76. data/lib/arjdbc/tasks/mssql_database_tasks.rb +126 -25
  77. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  78. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  79. data/lib/arjdbc/util/table_copier.rb +3 -1
  80. data/lib/arjdbc/version.rb +3 -1
  81. data/pom.xml +4 -4
  82. data/rakelib/01-tomcat.rake +2 -2
  83. data/rakelib/rails.rake +1 -1
  84. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  85. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  86. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +549 -691
  87. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
  88. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  89. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +125 -53
  90. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +97 -103
  91. data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
  92. metadata +10 -18
@@ -57,6 +57,7 @@ import org.jruby.RubyString;
57
57
  import org.jruby.RubySymbol;
58
58
  import org.jruby.RubyTime;
59
59
  import org.jruby.anno.JRubyMethod;
60
+ import org.jruby.runtime.Block;
60
61
  import org.jruby.runtime.ObjectAllocator;
61
62
  import org.jruby.runtime.ThreadContext;
62
63
  import org.jruby.runtime.builtin.IRubyObject;
@@ -203,6 +204,57 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
203
204
  });
204
205
  }
205
206
 
207
+ /**
208
+ * Executes an INSERT SQL statement
209
+ * @param context
210
+ * @param sql
211
+ * @param pk Rails PK
212
+ * @return ActiveRecord::Result
213
+ * @throws SQLException
214
+ */
215
+ @Override
216
+ @JRubyMethod(name = "execute_insert_pk", required = 2)
217
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
218
+
219
+ // MSSQL does not like composite primary keys here so chop it if there is more than one column
220
+ IRubyObject modifiedPk = pk;
221
+
222
+ if (pk instanceof RubyArray) {
223
+ RubyArray ary = (RubyArray) pk;
224
+ if (ary.size() > 0) {
225
+ modifiedPk = ary.eltInternal(0);
226
+ }
227
+ }
228
+
229
+ return super.execute_insert_pk(context, sql, modifiedPk);
230
+ }
231
+
232
+ /**
233
+ * Executes an INSERT SQL statement using a prepared statement
234
+ * @param context
235
+ * @param sql
236
+ * @param binds RubyArray of values to be bound to the query
237
+ * @param pk Rails PK
238
+ * @return ActiveRecord::Result
239
+ * @throws SQLException
240
+ */
241
+ @Override
242
+ @JRubyMethod(name = "execute_insert_pk", required = 3)
243
+ public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
244
+ final IRubyObject pk) {
245
+ // MSSQL does not like composite primary keys here so chop it if there is more than one column
246
+ IRubyObject modifiedPk = pk;
247
+
248
+ if (pk instanceof RubyArray) {
249
+ RubyArray ary = (RubyArray) pk;
250
+ if (ary.size() > 0) {
251
+ modifiedPk = ary.eltInternal(0);
252
+ }
253
+ }
254
+
255
+ return super.execute_insert_pk(context, sql, binds, modifiedPk);
256
+ }
257
+
206
258
  @Override
207
259
  protected Integer jdbcTypeFor(final String type) {
208
260
 
@@ -598,6 +650,42 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
598
650
  });
599
651
  }
600
652
 
653
+ // Get MSSQL minor version eg. 0
654
+ @JRubyMethod
655
+ public IRubyObject database_minor_version(final ThreadContext context) throws SQLException {
656
+ return withConnection(context, new Callable<IRubyObject>() {
657
+ public IRubyObject call(final Connection connection) throws SQLException {
658
+ final DatabaseMetaData metaData = connection.getMetaData();
659
+
660
+ return context.runtime.newFixnum( metaData.getDatabaseMinorVersion() );
661
+ }
662
+ });
663
+ }
664
+
665
+ // Get MSSQL product name eg. Microsoft SQL Server
666
+ @JRubyMethod
667
+ public IRubyObject database_product_name(final ThreadContext context) throws SQLException {
668
+ return withConnection(context, new Callable<IRubyObject>() {
669
+ public IRubyObject call(final Connection connection) throws SQLException {
670
+ final DatabaseMetaData metaData = connection.getMetaData();
671
+
672
+ return context.runtime.newString( metaData.getDatabaseProductName() );
673
+ }
674
+ });
675
+ }
676
+
677
+ // Get MSSQL product version eg. '14.00.3238'
678
+ @JRubyMethod
679
+ public IRubyObject database_product_version(final ThreadContext context) throws SQLException {
680
+ return withConnection(context, new Callable<IRubyObject>() {
681
+ public IRubyObject call(final Connection connection) throws SQLException {
682
+ final DatabaseMetaData metaData = connection.getMetaData();
683
+
684
+ return context.runtime.newString( metaData.getDatabaseProductVersion() );
685
+ }
686
+ });
687
+ }
688
+
601
689
  /**
602
690
  * Microsoft SQL 2000+ support schemas
603
691
  */
@@ -40,8 +40,6 @@ import java.sql.Statement;
40
40
  import java.sql.Timestamp;
41
41
  import java.sql.Types;
42
42
 
43
- import org.joda.time.DateTime;
44
- import org.joda.time.DateTimeZone;
45
43
  import org.jruby.*;
46
44
  import org.jruby.anno.JRubyMethod;
47
45
  import org.jruby.exceptions.RaiseException;
@@ -89,11 +87,9 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
89
87
 
90
88
  @JRubyMethod(name = { "full_version" })
91
89
  public IRubyObject db_version(final ThreadContext context) {
92
- return withConnection(context, new Callable<IRubyObject>() {
93
- public IRubyObject call(final Connection connection) throws SQLException {
94
- final DatabaseMetaData metaData = connection.getMetaData();
95
- return context.runtime.newString(metaData.getDatabaseProductVersion());
96
- }
90
+ return withConnection(context, (Callable<IRubyObject>) connection -> {
91
+ final DatabaseMetaData metaData = connection.getMetaData();
92
+ return context.runtime.newString(metaData.getDatabaseProductVersion());
97
93
  });
98
94
  }
99
95
 
@@ -108,7 +104,7 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
108
104
  if ( major < 5 ) {
109
105
  final RubyClass errorClass = getConnectionNotEstablished(context.runtime);
110
106
  throw context.runtime.newRaiseException(errorClass,
111
- "MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "");
107
+ "MySQL adapter requires driver >= 5.0 got: " + major + "." + minor);
112
108
  }
113
109
  if ( major == 5 && minor < 1 ) { // need 5.1 for JDBC 4.0
114
110
  // lightweight validation query: "/* ping */ SELECT 1"
@@ -203,7 +199,7 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
203
199
  return resultSet.wasNull() ? context.nil : RubyString.newEmptyString(runtime);
204
200
  }
205
201
 
206
- if ( rawDateTime != null && rawDateTime.booleanValue() ) {
202
+ if ( rawDateTime != null && rawDateTime) {
207
203
  return RubyString.newString(runtime, DateTimeUtils.dummyTimeToString(value));
208
204
  }
209
205
 
@@ -237,7 +233,7 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
237
233
  if (lowerCase == null) {
238
234
  lowerCase = lowerCaseIdentifiers = connection.getMetaData().storesLowerCaseIdentifiers();
239
235
  }
240
- return lowerCase.booleanValue() ? value.toLowerCase() : value;
236
+ return lowerCase ? value.toLowerCase() : value;
241
237
  }
242
238
 
243
239
  @Override
@@ -247,7 +243,9 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
247
243
  connection = super.newConnection();
248
244
  }
249
245
  catch (SQLException ex) {
250
- if (ex.getErrorCode() == 1049) throw newNoDatabaseError(ex);
246
+ int errorCode = ex.getErrorCode();
247
+ // access denied, no database
248
+ if (errorCode == 1044 || errorCode == 1049) throw newNoDatabaseError(ex);
251
249
  throw ex;
252
250
  }
253
251
  if ( doStopCleanupThread() ) shutdownCleanupThread();
@@ -261,7 +259,7 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
261
259
  }
262
260
 
263
261
  private static boolean doStopCleanupThread() throws SQLException {
264
- return stopCleanupThread != null && stopCleanupThread.booleanValue();
262
+ return stopCleanupThread != null && stopCleanupThread;
265
263
  }
266
264
 
267
265
  private static boolean cleanupThreadShutdown;
@@ -276,19 +274,11 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
276
274
  catch (ClassNotFoundException e) {
277
275
  debugMessage(null, "missing MySQL JDBC cleanup thread ", e);
278
276
  }
279
- catch (NoSuchMethodException e) {
280
- debugMessage(null, e);
281
- }
282
- catch (IllegalAccessException e) {
277
+ catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
283
278
  debugMessage(null, e);
284
- }
285
- catch (InvocationTargetException e) {
279
+ } catch (InvocationTargetException e) {
286
280
  debugMessage(null, e.getTargetException());
287
- }
288
- catch (SecurityException e) {
289
- debugMessage(null, e);
290
- }
291
- finally { cleanupThreadShutdown = true; }
281
+ } finally { cleanupThreadShutdown = true; }
292
282
  }
293
283
 
294
284
  }
@@ -35,27 +35,19 @@ import arjdbc.util.StringHelper;
35
35
  import java.io.ByteArrayInputStream;
36
36
  import java.lang.StringBuilder;
37
37
  import java.lang.reflect.InvocationTargetException;
38
- import java.sql.Connection;
39
- import java.sql.DatabaseMetaData;
38
+ import java.math.BigDecimal;
39
+ import java.sql.*;
40
40
  import java.sql.Date;
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;
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;
@@ -163,15 +156,12 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
163
156
  try { // public static String getVersion()
164
157
  final String version = (String) // "PostgreSQL 9.2 JDBC4 (build 1002)"
165
158
  jdbcDriver.getClass().getMethod("getVersion").invoke(null);
166
- if ( version != null && version.indexOf("JDBC3") >= 0 ) {
159
+ if ( version != null && version.contains("JDBC3")) {
167
160
  // config[:connection_alive_sql] ||= 'SELECT 1'
168
161
  setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("SELECT 1"));
169
162
  }
170
163
  }
171
- catch (NoSuchMethodException e) { }
172
- catch (SecurityException e) { }
173
- catch (IllegalAccessException e) { }
174
- catch (InvocationTargetException e) { }
164
+ catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException ignored) { }
175
165
  }
176
166
 
177
167
  return driverWrapper;
@@ -207,9 +197,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
207
197
  RubyClass arrayClass = oidArray(context);
208
198
  RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
209
199
  // The type or its delegate is an OID::Array
210
- if (arrayClass.isInstance(attributeType) ||
200
+ if (attributeType != null && (arrayClass.isInstance(attributeType) ||
211
201
  (attributeType.hasInstanceVariable("@delegate_dc_obj") &&
212
- arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
202
+ arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj"))))) {
213
203
  return "array";
214
204
  }
215
205
 
@@ -218,11 +208,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
218
208
 
219
209
  @JRubyMethod(name = "database_product")
220
210
  public IRubyObject database_product(final ThreadContext context) {
221
- return withConnection(context, new Callable<IRubyObject>() {
222
- public IRubyObject call(final Connection connection) throws SQLException {
223
- final DatabaseMetaData metaData = connection.getMetaData();
224
- return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
225
- }
211
+ return withConnection(context, (Callable<IRubyObject>) connection -> {
212
+ final DatabaseMetaData metaData = connection.getMetaData();
213
+ return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
226
214
  });
227
215
  }
228
216
 
@@ -308,6 +296,24 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
308
296
  statement.setArray(index, connection.createArrayOf(typeName, values));
309
297
  }
310
298
 
299
+ protected void setDecimalParameter(final ThreadContext context,
300
+ final Connection connection, final PreparedStatement statement,
301
+ final int index, final IRubyObject value,
302
+ final IRubyObject attribute, final int type) throws SQLException {
303
+ if (value instanceof RubyBigDecimal) {
304
+ RubyBigDecimal bigDecimal = (RubyBigDecimal) value;
305
+
306
+ // too bad RubyBigDecimal.isNaN() isn't public
307
+ if (bigDecimal.nan_p(context) == context.tru) {
308
+ statement.setDouble(index, Double.NaN);
309
+ } else {
310
+ statement.setBigDecimal(index, bigDecimal.getValue());
311
+ }
312
+ } else {
313
+ super.setDecimalParameter(context, connection, statement, index, value, attribute, type);
314
+ }
315
+ }
316
+
311
317
  @Override
312
318
  protected void setBlobParameter(final ThreadContext context,
313
319
  final Connection connection, final PreparedStatement statement,
@@ -377,14 +383,16 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
377
383
  value = value.callMethod(context, "to_date");
378
384
  }
379
385
 
380
- int year = RubyNumeric.num2int(value.callMethod(context, "year"));
381
- int month = RubyNumeric.num2int(value.callMethod(context, "month"));
382
- int day = RubyNumeric.num2int(value.callMethod(context, "day"));
383
-
384
- @SuppressWarnings("deprecated")
385
- Date date = new Date(year - 1900, month - 1, day);
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
+ }
386
393
 
387
- statement.setDate(index, date);
394
+ // NOTE: assuming Date#to_s does right ...
395
+ statement.setDate(index, Date.valueOf(value.toString()));
388
396
  }
389
397
 
390
398
  @Override
@@ -428,7 +436,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
428
436
  break;
429
437
 
430
438
  case "interval":
431
- statement.setObject(index, new PGInterval(value.toString()));
439
+ statement.setObject(index, stringToPGInterval(value.toString()));
432
440
  break;
433
441
 
434
442
  case "json":
@@ -485,6 +493,74 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
485
493
  }
486
494
  }
487
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
+
488
564
  protected IRubyObject jdbcToRuby(ThreadContext context, Ruby runtime, int column, int type, ResultSet resultSet) throws SQLException {
489
565
  return typeMap != null ?
490
566
  convertWithTypeMap(context, runtime, column, type, resultSet) :
@@ -514,7 +590,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
514
590
 
515
591
  private static Double[] parseDoubles(IRubyObject value) {
516
592
  Matcher matches = doubleValuePattern.matcher(value.toString());
517
- ArrayList<Double> doubles = new ArrayList<Double>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
593
+ ArrayList<Double> doubles = new ArrayList<>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
518
594
 
519
595
  while ( matches.find() ) {
520
596
  doubles.add(Double.parseDouble(matches.group()));
@@ -729,6 +805,11 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
729
805
  return DateTimeUtils.parseDate(context, value, getDefaultTimeZone(context));
730
806
  }
731
807
 
808
+ protected IRubyObject decimalToRuby(final ThreadContext context,
809
+ final Ruby runtime, final ResultSet resultSet, final int column) throws SQLException {
810
+ if ("NaN".equals(resultSet.getString(column))) return new RubyBigDecimal(runtime, BigDecimal.ZERO, true);
811
+ return super.decimalToRuby(context, runtime, resultSet, column);
812
+ }
732
813
 
733
814
  /**
734
815
  * Detects PG specific types and converts them to their Ruby equivalents
@@ -861,34 +942,25 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
861
942
  private static String formatInterval(final Object object) {
862
943
  final PGInterval interval = (PGInterval) object;
863
944
  final StringBuilder str = new StringBuilder(32);
945
+ str.append("P");
864
946
 
865
947
  final int years = interval.getYears();
866
- if (years != 0) str.append(years).append(" years ");
948
+ if (years != 0) str.append(years).append("Y");
867
949
 
868
950
  final int months = interval.getMonths();
869
- if (months != 0) str.append(months).append(" months ");
951
+ if (months != 0) str.append(months).append("M");
870
952
 
871
953
  final int days = interval.getDays();
872
- if (days != 0) str.append(days).append(" days ");
954
+ if (days != 0) str.append(days).append("D");
873
955
 
874
956
  final int hours = interval.getHours();
875
957
  final int mins = interval.getMinutes();
876
- final int secs = (int) interval.getSeconds();
877
- if (hours != 0 || mins != 0 || secs != 0) { // xx:yy:zz if not all 00
878
- if (hours < 10) str.append('0');
879
-
880
- str.append(hours).append(':');
881
-
882
- if (mins < 10) str.append('0');
883
-
884
- str.append(mins).append(':');
885
-
886
- if (secs < 10) str.append('0');
887
-
888
- str.append(secs);
889
-
890
- } else if (str.length() > 1) {
891
- 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");
892
964
  }
893
965
 
894
966
  return str.toString();