activerecord-jdbc-alt-adapter 52.5.1-java → 60.2.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.nvimlog +0 -0
  4. data/.travis.yml +61 -37
  5. data/Gemfile +10 -3
  6. data/README.md +44 -28
  7. data/Rakefile +1 -1
  8. data/Rakefile.jdbc +8 -1
  9. data/activerecord-jdbc-adapter.gemspec +5 -8
  10. data/activerecord-jdbc-alt-adapter.gemspec +5 -8
  11. data/lib/arel/visitors/sqlserver.rb +33 -23
  12. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  13. data/lib/arjdbc/abstract/core.rb +16 -23
  14. data/lib/arjdbc/abstract/database_statements.rb +24 -0
  15. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  16. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  17. data/lib/arjdbc/db2/column.rb +0 -39
  18. data/lib/arjdbc/derby/adapter.rb +1 -20
  19. data/lib/arjdbc/firebird/adapter.rb +0 -21
  20. data/lib/arjdbc/h2/adapter.rb +0 -15
  21. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  22. data/lib/arjdbc/informix/adapter.rb +0 -23
  23. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  24. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  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/adapter.rb +105 -36
  40. data/lib/arjdbc/mssql/column.rb +5 -1
  41. data/lib/arjdbc/mssql/connection_methods.rb +8 -2
  42. data/lib/arjdbc/mssql/database_limits.rb +2 -0
  43. data/lib/arjdbc/mssql/database_statements.rb +43 -5
  44. data/lib/arjdbc/mssql/errors.rb +2 -0
  45. data/lib/arjdbc/mssql/explain_support.rb +3 -1
  46. data/lib/arjdbc/mssql/extensions/attribute_methods.rb +5 -1
  47. data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
  48. data/lib/arjdbc/mssql/quoting.rb +38 -0
  49. data/lib/arjdbc/mssql/schema_creation.rb +24 -2
  50. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  51. data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
  52. data/lib/arjdbc/mssql/schema_statements.rb +63 -21
  53. data/lib/arjdbc/mssql/transaction.rb +2 -0
  54. data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
  55. data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
  56. data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
  57. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
  58. data/lib/arjdbc/mssql/types/string_types.rb +2 -0
  59. data/lib/arjdbc/mssql/types.rb +2 -0
  60. data/lib/arjdbc/mssql/utils.rb +2 -0
  61. data/lib/arjdbc/mssql.rb +3 -1
  62. data/lib/arjdbc/mysql/adapter.rb +47 -18
  63. data/lib/arjdbc/postgresql/adapter.rb +240 -214
  64. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  65. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  66. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  67. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  68. data/lib/arjdbc/postgresql/column.rb +6 -4
  69. data/lib/arjdbc/postgresql/name.rb +2 -0
  70. data/lib/arjdbc/postgresql/oid_types.rb +3 -1
  71. data/lib/arjdbc/sqlite3/adapter.rb +188 -180
  72. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -4
  73. data/lib/arjdbc/tasks/databases.rake +13 -10
  74. data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
  75. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  76. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  77. data/lib/arjdbc/util/table_copier.rb +3 -1
  78. data/lib/arjdbc/version.rb +1 -1
  79. data/pom.xml +4 -4
  80. data/rakelib/01-tomcat.rake +2 -2
  81. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  82. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  83. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +406 -629
  84. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
  85. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  86. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +56 -30
  87. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  88. data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
  89. metadata +7 -16
@@ -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;
@@ -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,29 @@ 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"));
386
+ if (value instanceof RubyDate) {
387
+ RubyDate rubyDate = (RubyDate) value;
388
+ DateTime dt = rubyDate.getDateTime();
389
+ // pgjdbc needs adjustment for default JVM timezone
390
+ //
391
+ // If the date is a day when Daylight savings starts (2am changed to 3am),
392
+ // And we are in a positive GMT timezone (Australia/Melbourne)
393
+ // Then removing milliseconds equal to the TimeZone (+11 GMT),
394
+ // will result in the date being the previous day 23:00, because of the missing hour.
395
+ // So we check if the date after the shift is outside of daylight time and remove an hours worth of milliseconds.
396
+ final long dateMillis = dt.getMillis();
397
+ long offset = TZ_DEFAULT.getOffset(dt.getMillis());
398
+ if (TZ_DEFAULT.inDaylightTime(new Date(dt.getMillis())) && !TZ_DEFAULT.inDaylightTime(new Date(dateMillis - offset))) {
399
+ offset -= 3600000; // 1 hour
400
+ }
401
+ Date utcShiftedDate = new Date(dateMillis - offset);
383
402
 
384
- @SuppressWarnings("deprecated")
385
- Date date = new Date(year - 1900, month - 1, day);
403
+ statement.setDate(index, utcShiftedDate);
404
+ return;
405
+ }
386
406
 
387
- statement.setDate(index, date);
407
+ // NOTE: assuming Date#to_s does right ...
408
+ statement.setDate(index, Date.valueOf(value.toString()));
388
409
  }
389
410
 
390
411
  @Override
@@ -514,7 +535,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
514
535
 
515
536
  private static Double[] parseDoubles(IRubyObject value) {
516
537
  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
538
+ ArrayList<Double> doubles = new ArrayList<>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
518
539
 
519
540
  while ( matches.find() ) {
520
541
  doubles.add(Double.parseDouble(matches.group()));
@@ -729,6 +750,11 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
729
750
  return DateTimeUtils.parseDate(context, value, getDefaultTimeZone(context));
730
751
  }
731
752
 
753
+ protected IRubyObject decimalToRuby(final ThreadContext context,
754
+ final Ruby runtime, final ResultSet resultSet, final int column) throws SQLException {
755
+ if ("NaN".equals(resultSet.getString(column))) return new RubyBigDecimal(runtime, BigDecimal.ZERO, true);
756
+ return super.decimalToRuby(context, runtime, resultSet, column);
757
+ }
732
758
 
733
759
  /**
734
760
  * Detects PG specific types and converts them to their Ruby equivalents
@@ -100,55 +100,53 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
100
100
  public IRubyObject encoding(final ThreadContext context) throws SQLException {
101
101
  if (encoding != null) return encoding;
102
102
 
103
- return withConnection(context, new Callable<IRubyObject>() {
104
- // FIXME: How many single result queries do we have in Java?
105
- public IRubyObject call(final Connection connection) throws SQLException {
106
- String query = "PRAGMA encoding";
107
- Statement statement = null;
108
- ResultSet resultSet = null;
109
- try {
110
- statement = createStatement(context, connection);
111
- if (statement.execute(query)) {
112
- // Enebo: I do not think we need to worry about failure here?
113
- String encodingString = statement.getResultSet().getString(1);
114
-
115
- encoding = cachedString(context, encodingString);
116
-
117
- return encoding;
118
- }
119
- } catch (final SQLException e) {
120
- debugErrorSQL(context, query);
121
- throw e;
122
- } finally {
123
- close(resultSet);
124
- close(statement);
103
+ // FIXME: How many single result queries do we have in Java?
104
+ return withConnection(context, connection -> {
105
+ String query = "PRAGMA encoding";
106
+ Statement statement = null;
107
+ ResultSet resultSet = null;
108
+ try {
109
+ statement = createStatement(context, connection);
110
+ if (statement.execute(query)) {
111
+ // Enebo: I do not think we need to worry about failure here?
112
+ resultSet = statement.getResultSet();
113
+ if (!resultSet.next()) return context.nil;
114
+ String encodingString = resultSet.getString(1);
115
+
116
+ encoding = cachedString(context, encodingString);
117
+
118
+ return encoding;
125
119
  }
126
-
127
- return context.nil;
120
+ } catch (final SQLException e) {
121
+ debugErrorSQL(context, query);
122
+ throw e;
123
+ } finally {
124
+ close(resultSet);
125
+ close(statement);
128
126
  }
127
+
128
+ return context.nil;
129
129
  });
130
130
  }
131
131
 
132
132
  @JRubyMethod(name = {"last_insert_rowid", "last_insert_id"}, alias = "last_insert_row_id")
133
133
  public IRubyObject last_insert_rowid(final ThreadContext context)
134
134
  throws SQLException {
135
- return withConnection(context, new Callable<IRubyObject>() {
136
- public IRubyObject call(final Connection connection) throws SQLException {
137
- Statement statement = null; ResultSet genKeys = null;
138
- try {
139
- statement = connection.createStatement();
140
- // NOTE: strangely this will work and has been used for quite some time :
141
- //return mapGeneratedKeys(context.getRuntime(), connection, statement, true);
142
- // but we should assume SQLite JDBC will prefer sane API usage eventually :
143
- genKeys = statement.executeQuery("SELECT last_insert_rowid()");
144
- return doMapGeneratedKeys(context.runtime, genKeys, true);
145
- }
146
- catch (final SQLException e) {
147
- debugMessage(context.runtime, "failed to get generated keys: ", e);
148
- throw e;
149
- }
150
- finally { close(genKeys); close(statement); }
135
+ return withConnection(context, connection -> {
136
+ Statement statement = null; ResultSet genKeys = null;
137
+ try {
138
+ statement = connection.createStatement();
139
+ // NOTE: strangely this will work and has been used for quite some time :
140
+ //return mapGeneratedKeys(context.getRuntime(), connection, statement, true);
141
+ // but we should assume SQLite JDBC will prefer sane API usage eventually :
142
+ genKeys = statement.executeQuery("SELECT last_insert_rowid()");
143
+ return doMapGeneratedKeys(context.runtime, genKeys, true);
144
+ }
145
+ catch (final SQLException e) {
146
+ debugMessage(context.runtime, "failed to get generated keys: ", e);
147
+ throw e;
151
148
  }
149
+ finally { close(genKeys); close(statement); }
152
150
  });
153
151
  }
154
152
 
@@ -183,66 +181,64 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
183
181
  final String tableName = table;
184
182
  final String schemaName = schema;
185
183
  // return super.indexes(context, tableName, name, schemaName);
186
- return withConnection(context, new Callable<IRubyObject>() {
187
- public RubyArray call(final Connection connection) throws SQLException {
188
- final Ruby runtime = context.runtime;
189
- final RubyClass IndexDefinition = getIndexDefinition(runtime);
184
+ return withConnection(context, (Callable<IRubyObject>) connection -> {
185
+ final Ruby runtime = context.runtime;
186
+ final RubyClass IndexDefinition = getIndexDefinition(runtime);
190
187
 
191
- final TableName table = extractTableName(connection, null, schemaName, tableName);
188
+ final TableName table1 = extractTableName(connection, null, schemaName, tableName);
192
189
 
193
- final List<RubyString> primaryKeys = primaryKeys(context, connection, table);
190
+ final List<RubyString> primaryKeys = primaryKeys(context, connection, table1);
194
191
 
195
- final DatabaseMetaData metaData = connection.getMetaData();
196
- ResultSet indexInfoSet;
197
- try {
198
- indexInfoSet = metaData.getIndexInfo(table.catalog, table.schema, table.name, false, true);
199
- }
200
- catch (SQLException e) {
201
- final String msg = e.getMessage();
202
- if ( msg != null && msg.startsWith("[SQLITE_ERROR] SQL error or missing database") ) {
203
- return RubyArray.newEmptyArray(runtime); // on 3.8.7 getIndexInfo fails if table has no indexes
204
- }
205
- throw e;
192
+ final DatabaseMetaData metaData = connection.getMetaData();
193
+ ResultSet indexInfoSet;
194
+ try {
195
+ indexInfoSet = metaData.getIndexInfo(table1.catalog, table1.schema, table1.name, false, true);
196
+ }
197
+ catch (SQLException e) {
198
+ final String msg = e.getMessage();
199
+ if ( msg != null && msg.startsWith("[SQLITE_ERROR] SQL error or missing database") ) {
200
+ return RubyArray.newEmptyArray(runtime); // on 3.8.7 getIndexInfo fails if table has no indexes
206
201
  }
207
- final RubyArray indexes = RubyArray.newArray(runtime, 8);
208
- try {
209
- String currentIndex = null;
210
-
211
- while ( indexInfoSet.next() ) {
212
- String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
213
- if ( indexName == null ) continue;
214
- RubyArray currentColumns = null;
202
+ throw e;
203
+ }
204
+ final RubyArray indexes = RubyArray.newArray(runtime, 8);
205
+ try {
206
+ String currentIndex = null;
215
207
 
216
- final String columnName = indexInfoSet.getString(INDEX_INFO_COLUMN_NAME);
217
- final RubyString rubyColumnName = cachedString(context, columnName);
218
- if ( primaryKeys.contains(rubyColumnName) ) continue;
208
+ while ( indexInfoSet.next() ) {
209
+ String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
210
+ if ( indexName == null ) continue;
211
+ RubyArray currentColumns = null;
219
212
 
220
- // We are working on a new index
221
- if ( ! indexName.equals(currentIndex) ) {
222
- currentIndex = indexName;
213
+ final String columnName = indexInfoSet.getString(INDEX_INFO_COLUMN_NAME);
214
+ final RubyString rubyColumnName = cachedString(context, columnName);
215
+ if ( primaryKeys.contains(rubyColumnName) ) continue;
223
216
 
224
- String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
217
+ // We are working on a new index
218
+ if ( ! indexName.equals(currentIndex) ) {
219
+ currentIndex = indexName;
225
220
 
226
- final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
221
+ String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
227
222
 
228
- IRubyObject[] args = new IRubyObject[] {
229
- cachedString(context, indexTableName), // table_name
230
- cachedString(context, indexName), // index_name
231
- nonUnique ? runtime.getFalse() : runtime.getTrue(), // unique
232
- currentColumns = RubyArray.newArray(runtime, 4) // [] column names
233
- };
223
+ final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
234
224
 
235
- indexes.append( IndexDefinition.newInstance(context, args, Block.NULL_BLOCK) ); // IndexDefinition.new
236
- }
225
+ IRubyObject[] args = new IRubyObject[] {
226
+ cachedString(context, indexTableName), // table_name
227
+ cachedString(context, indexName), // index_name
228
+ nonUnique ? context.fals : context.tru, // unique
229
+ currentColumns = RubyArray.newArray(runtime, 4) // [] column names
230
+ };
237
231
 
238
- // one or more columns can be associated with an index
239
- if ( currentColumns != null ) currentColumns.append(rubyColumnName);
232
+ indexes.append( IndexDefinition.newInstance(context, args, Block.NULL_BLOCK) ); // IndexDefinition.new
240
233
  }
241
234
 
242
- return indexes;
235
+ // one or more columns can be associated with an index
236
+ if ( currentColumns != null ) currentColumns.append(rubyColumnName);
237
+ }
243
238
 
244
- } finally { close(indexInfoSet); }
245
- }
239
+ return indexes;
240
+
241
+ } finally { close(indexInfoSet); }
246
242
  });
247
243
  }
248
244
 
@@ -390,8 +386,9 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
390
386
  "create_savepoint (without name) not implemented!"
391
387
  );
392
388
  }
393
- final Connection connection = getConnection(true); Statement statement = null;
389
+ Statement statement = null;
394
390
  try {
391
+ final Connection connection = getConnectionInternal(true);
395
392
  connection.setAutoCommit(false);
396
393
  // NOTE: JDBC driver does not support setSavepoint(String) :
397
394
  ( statement = connection.createStatement() ).execute("SAVEPOINT " + name.toString());
@@ -411,12 +408,13 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
411
408
  public IRubyObject rollback_savepoint(final ThreadContext context, final IRubyObject name) {
412
409
  if ( useSavepointAPI(context) ) return super.rollback_savepoint(context, name);
413
410
 
414
- final Connection connection = getConnection(true); Statement statement = null;
411
+ Statement statement = null;
415
412
  try {
416
413
  if ( getSavepoints(context).get(name) == null ) {
417
414
  throw newSavepointNotSetError(context, name, "rollback");
418
415
  }
419
416
  // NOTE: JDBC driver does not implement rollback(Savepoint) :
417
+ final Connection connection = getConnectionInternal(true);
420
418
  ( statement = connection.createStatement() ).execute("ROLLBACK TO SAVEPOINT " + name.toString());
421
419
 
422
420
  return context.nil;
@@ -434,12 +432,13 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
434
432
  public IRubyObject release_savepoint(final ThreadContext context, final IRubyObject name) {
435
433
  if ( useSavepointAPI(context) ) return super.release_savepoint(context, name);
436
434
 
437
- final Connection connection = getConnection(true); Statement statement = null;
435
+ Statement statement = null;
438
436
  try {
439
437
  if ( getSavepoints(context).remove(name) == null ) {
440
438
  throw newSavepointNotSetError(context, name, "release");
441
439
  }
442
440
  // NOTE: JDBC driver does not implement release(Savepoint) :
441
+ final Connection connection = getConnectionInternal(true);
443
442
  ( statement = connection.createStatement() ).execute("RELEASE SAVEPOINT " + name.toString());
444
443
  return context.nil;
445
444
  } catch (SQLException e) {
@@ -453,19 +452,15 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
453
452
  // a consistent JDBC layer.
454
453
  @JRubyMethod(name = "supports_savepoints?")
455
454
  public IRubyObject supports_savepoints_p(final ThreadContext context) throws SQLException {
456
- return context.runtime.getTrue();
455
+ return context.tru;
457
456
  }
458
457
 
459
- @Override
460
- protected void setBooleanParameter(final ThreadContext context,
461
- final Connection connection, final PreparedStatement statement,
462
- final int index, final IRubyObject value,
463
- final IRubyObject attribute, final int type) throws SQLException {
464
- // Apparently active record stores booleans in sqlite as 't' and 'f' instead of the built in 1/0
465
- statement.setString(index, value.isTrue() ? "t" : "f");
458
+ @JRubyMethod(name = "readonly?")
459
+ public IRubyObject readonly_p(final ThreadContext context) throws SQLException {
460
+ final Connection connection = getConnection(true);
461
+ return context.runtime.newBoolean(connection.isReadOnly());
466
462
  }
467
463
 
468
-
469
464
  @Override
470
465
  protected void setDecimalParameter(final ThreadContext context,
471
466
  final Connection connection, final PreparedStatement statement,
@@ -518,7 +513,7 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
518
513
  final int index, IRubyObject value,
519
514
  final IRubyObject attribute, final int type) throws SQLException {
520
515
 
521
- if (value instanceof RubyTime) value = ((RubyTime) value).strftime(TIMESTAMP_FORMAT);
516
+ if (value instanceof RubyTime) value = ((RubyTime) value).strftime(context, TIMESTAMP_FORMAT);
522
517
 
523
518
  setStringParameter(context, connection, statement, index, value, attribute, type);
524
519
  }