activerecord-jdbc-adapter 52.7-java → 60.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/.travis.yml +58 -37
  4. data/Gemfile +9 -2
  5. data/README.md +25 -9
  6. data/Rakefile +1 -1
  7. data/Rakefile.jdbc +8 -1
  8. data/activerecord-jdbc-adapter.gemspec +5 -8
  9. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  10. data/lib/arjdbc/abstract/core.rb +16 -23
  11. data/lib/arjdbc/abstract/database_statements.rb +26 -2
  12. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  13. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  14. data/lib/arjdbc/db2/column.rb +0 -39
  15. data/lib/arjdbc/derby/adapter.rb +1 -20
  16. data/lib/arjdbc/firebird/adapter.rb +0 -21
  17. data/lib/arjdbc/h2/adapter.rb +0 -15
  18. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  19. data/lib/arjdbc/informix/adapter.rb +0 -23
  20. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  21. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  22. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  23. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  24. data/lib/arjdbc/jdbc/column.rb +2 -0
  25. data/lib/arjdbc/jdbc/connection.rb +2 -0
  26. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  27. data/lib/arjdbc/jdbc/error.rb +2 -0
  28. data/lib/arjdbc/jdbc/extension.rb +2 -0
  29. data/lib/arjdbc/jdbc/java.rb +3 -1
  30. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  31. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  32. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  33. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  34. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  35. data/lib/arjdbc/mysql/adapter.rb +47 -18
  36. data/lib/arjdbc/mysql/connection_methods.rb +0 -1
  37. data/lib/arjdbc/postgresql/adapter.rb +220 -213
  38. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  39. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  40. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  41. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  42. data/lib/arjdbc/postgresql/column.rb +6 -4
  43. data/lib/arjdbc/postgresql/connection_methods.rb +0 -1
  44. data/lib/arjdbc/postgresql/name.rb +2 -0
  45. data/lib/arjdbc/postgresql/oid_types.rb +2 -0
  46. data/lib/arjdbc/sqlite3/adapter.rb +175 -180
  47. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -5
  48. data/lib/arjdbc/tasks/databases.rake +13 -10
  49. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  50. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  51. data/lib/arjdbc/util/table_copier.rb +3 -1
  52. data/lib/arjdbc/version.rb +1 -1
  53. data/pom.xml +4 -4
  54. data/rakelib/01-tomcat.rake +2 -2
  55. data/rakelib/rails.rake +1 -1
  56. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  57. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  58. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +434 -701
  59. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +0 -51
  60. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  61. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +31 -24
  62. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  63. metadata +8 -10
@@ -200,57 +200,6 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
200
200
  });
201
201
  }
202
202
 
203
- /**
204
- * Executes an INSERT SQL statement
205
- * @param context
206
- * @param sql
207
- * @param pk Rails PK
208
- * @return ActiveRecord::Result
209
- * @throws SQLException
210
- */
211
- @Override
212
- @JRubyMethod(name = "execute_insert_pk", required = 2)
213
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
214
-
215
- // MSSQL does not like composite primary keys here so chop it if there is more than one column
216
- IRubyObject modifiedPk = pk;
217
-
218
- if (pk instanceof RubyArray) {
219
- RubyArray ary = (RubyArray) pk;
220
- if (ary.size() > 0) {
221
- modifiedPk = ary.eltInternal(0);
222
- }
223
- }
224
-
225
- return super.execute_insert_pk(context, sql, modifiedPk);
226
- }
227
-
228
- /**
229
- * Executes an INSERT SQL statement using a prepared statement
230
- * @param context
231
- * @param sql
232
- * @param binds RubyArray of values to be bound to the query
233
- * @param pk Rails PK
234
- * @return ActiveRecord::Result
235
- * @throws SQLException
236
- */
237
- @Override
238
- @JRubyMethod(name = "execute_insert_pk", required = 3)
239
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
240
- final IRubyObject pk) {
241
- // MSSQL does not like composite primary keys here so chop it if there is more than one column
242
- IRubyObject modifiedPk = pk;
243
-
244
- if (pk instanceof RubyArray) {
245
- RubyArray ary = (RubyArray) pk;
246
- if (ary.size() > 0) {
247
- modifiedPk = ary.eltInternal(0);
248
- }
249
- }
250
-
251
- return super.execute_insert_pk(context, sql, binds, modifiedPk);
252
- }
253
-
254
203
  @Override
255
204
  protected Integer jdbcTypeFor(final String type) {
256
205
 
@@ -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,9 +35,9 @@ 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.math.BigDecimal;
38
39
  import java.sql.Connection;
39
40
  import java.sql.DatabaseMetaData;
40
- import java.sql.Date;
41
41
  import java.sql.PreparedStatement;
42
42
  import java.sql.ResultSet;
43
43
  import java.sql.ResultSetMetaData;
@@ -163,15 +163,12 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
163
163
  try { // public static String getVersion()
164
164
  final String version = (String) // "PostgreSQL 9.2 JDBC4 (build 1002)"
165
165
  jdbcDriver.getClass().getMethod("getVersion").invoke(null);
166
- if ( version != null && version.indexOf("JDBC3") >= 0 ) {
166
+ if ( version != null && version.contains("JDBC3")) {
167
167
  // config[:connection_alive_sql] ||= 'SELECT 1'
168
168
  setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("SELECT 1"));
169
169
  }
170
170
  }
171
- catch (NoSuchMethodException e) { }
172
- catch (SecurityException e) { }
173
- catch (IllegalAccessException e) { }
174
- catch (InvocationTargetException e) { }
171
+ catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException ignored) { }
175
172
  }
176
173
 
177
174
  return driverWrapper;
@@ -218,11 +215,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
218
215
 
219
216
  @JRubyMethod(name = "database_product")
220
217
  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
- }
218
+ return withConnection(context, (Callable<IRubyObject>) connection -> {
219
+ final DatabaseMetaData metaData = connection.getMetaData();
220
+ return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
226
221
  });
227
222
  }
228
223
 
@@ -308,6 +303,24 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
308
303
  statement.setArray(index, connection.createArrayOf(typeName, values));
309
304
  }
310
305
 
306
+ protected void setDecimalParameter(final ThreadContext context,
307
+ final Connection connection, final PreparedStatement statement,
308
+ final int index, final IRubyObject value,
309
+ final IRubyObject attribute, final int type) throws SQLException {
310
+ if (value instanceof RubyBigDecimal) {
311
+ RubyBigDecimal bigDecimal = (RubyBigDecimal) value;
312
+
313
+ // too bad RubyBigDecimal.isNaN() isn't public
314
+ if (bigDecimal.nan_p(context) == context.tru) {
315
+ statement.setDouble(index, Double.NaN);
316
+ } else {
317
+ statement.setBigDecimal(index, bigDecimal.getValue());
318
+ }
319
+ } else {
320
+ super.setDecimalParameter(context, connection, statement, index, value, attribute, type);
321
+ }
322
+ }
323
+
311
324
  @Override
312
325
  protected void setBlobParameter(final ThreadContext context,
313
326
  final Connection connection, final PreparedStatement statement,
@@ -373,18 +386,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
373
386
  }
374
387
  }
375
388
 
376
- if ( ! "Date".equals(value.getMetaClass().getName()) && value.respondsTo("to_date") ) {
377
- value = value.callMethod(context, "to_date");
378
- }
379
-
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
-
387
- statement.setDate(index, date);
389
+ super.setDateParameter(context, connection, statement, index, value, attribute, type);
388
390
  }
389
391
 
390
392
  @Override
@@ -514,7 +516,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
514
516
 
515
517
  private static Double[] parseDoubles(IRubyObject value) {
516
518
  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
519
+ ArrayList<Double> doubles = new ArrayList<>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
518
520
 
519
521
  while ( matches.find() ) {
520
522
  doubles.add(Double.parseDouble(matches.group()));
@@ -729,6 +731,11 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
729
731
  return DateTimeUtils.parseDate(context, value, getDefaultTimeZone(context));
730
732
  }
731
733
 
734
+ protected IRubyObject decimalToRuby(final ThreadContext context,
735
+ final Ruby runtime, final ResultSet resultSet, final int column) throws SQLException {
736
+ if ("NaN".equals(resultSet.getString(column))) return new RubyBigDecimal(runtime, BigDecimal.ZERO, true);
737
+ return super.decimalToRuby(context, runtime, resultSet, column);
738
+ }
732
739
 
733
740
  /**
734
741
  * 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
  }