activerecord-jdbc-alt-adapter 52.5.1-java → 60.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
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
  }