activerecord-jdbc-alt-adapter 52.2.3-java → 60.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +80 -52
  4. data/Gemfile +10 -3
  5. data/README.md +55 -37
  6. data/Rakefile +31 -5
  7. data/Rakefile.jdbc +8 -1
  8. data/activerecord-jdbc-adapter.gemspec +6 -9
  9. data/activerecord-jdbc-alt-adapter.gemspec +6 -9
  10. data/lib/arel/visitors/sqlserver.rb +33 -23
  11. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  12. data/lib/arjdbc/abstract/core.rb +17 -24
  13. data/lib/arjdbc/abstract/database_statements.rb +31 -20
  14. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  15. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  16. data/lib/arjdbc/db2/column.rb +0 -39
  17. data/lib/arjdbc/derby/adapter.rb +1 -20
  18. data/lib/arjdbc/firebird/adapter.rb +0 -21
  19. data/lib/arjdbc/h2/adapter.rb +0 -15
  20. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  21. data/lib/arjdbc/informix/adapter.rb +0 -23
  22. data/lib/arjdbc/jdbc.rb +0 -4
  23. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  24. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  25. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  26. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  27. data/lib/arjdbc/jdbc/column.rb +3 -5
  28. data/lib/arjdbc/jdbc/connection.rb +2 -0
  29. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  30. data/lib/arjdbc/jdbc/error.rb +2 -0
  31. data/lib/arjdbc/jdbc/extension.rb +2 -0
  32. data/lib/arjdbc/jdbc/java.rb +3 -1
  33. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  34. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  35. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  36. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  37. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  38. data/lib/arjdbc/mssql.rb +3 -1
  39. data/lib/arjdbc/mssql/adapter.rb +112 -46
  40. data/lib/arjdbc/mssql/column.rb +5 -1
  41. data/lib/arjdbc/mssql/connection_methods.rb +13 -2
  42. data/lib/arjdbc/mssql/database_limits.rb +2 -0
  43. data/lib/arjdbc/mssql/database_statements.rb +44 -6
  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.rb +2 -0
  55. data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
  56. data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
  57. data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
  58. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
  59. data/lib/arjdbc/mssql/types/string_types.rb +2 -0
  60. data/lib/arjdbc/mssql/utils.rb +2 -0
  61. data/lib/arjdbc/mysql/adapter.rb +47 -18
  62. data/lib/arjdbc/mysql/connection_methods.rb +13 -7
  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 +11 -6
  69. data/lib/arjdbc/postgresql/connection_methods.rb +3 -1
  70. data/lib/arjdbc/postgresql/name.rb +2 -0
  71. data/lib/arjdbc/postgresql/oid_types.rb +3 -1
  72. data/lib/arjdbc/sqlite3/adapter.rb +188 -180
  73. data/lib/arjdbc/sqlite3/connection_methods.rb +16 -4
  74. data/lib/arjdbc/tasks/databases.rake +13 -10
  75. data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
  76. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  77. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  78. data/lib/arjdbc/util/table_copier.rb +3 -1
  79. data/lib/arjdbc/version.rb +1 -1
  80. data/pom.xml +4 -4
  81. data/rakelib/01-tomcat.rake +2 -2
  82. data/rakelib/02-test.rake +0 -2
  83. data/rakelib/rails.rake +1 -1
  84. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  85. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  86. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +468 -637
  87. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +319 -38
  88. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  89. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +44 -31
  90. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  91. data/src/java/arjdbc/util/DateTimeUtils.java +34 -12
  92. metadata +7 -17
@@ -29,23 +29,35 @@ import arjdbc.jdbc.Callable;
29
29
  import arjdbc.jdbc.RubyJdbcConnection;
30
30
  import arjdbc.util.DateTimeUtils;
31
31
 
32
+ import java.lang.reflect.InvocationTargetException;
33
+ import java.lang.reflect.Method;
32
34
  import java.sql.Connection;
33
35
  import java.sql.DatabaseMetaData;
36
+ import java.sql.Date;
34
37
  import java.sql.PreparedStatement;
35
38
  import java.sql.ResultSet;
36
39
  import java.sql.Savepoint;
40
+ import java.sql.Statement;
37
41
  import java.sql.SQLException;
38
- import java.sql.Types;
39
42
  import java.sql.Timestamp;
43
+ import java.sql.Types;
40
44
  import java.util.Locale;
45
+ import java.util.ArrayList;
46
+ import java.util.HashMap;
47
+ import java.util.List;
48
+ import java.util.Map;
41
49
 
50
+ import org.joda.time.DateTime;
51
+ import org.joda.time.DateTimeZone;
42
52
  import org.jruby.Ruby;
43
53
  import org.jruby.RubyArray;
44
54
  import org.jruby.RubyBoolean;
45
55
  import org.jruby.RubyClass;
46
56
  import org.jruby.RubyString;
47
57
  import org.jruby.RubySymbol;
58
+ import org.jruby.RubyTime;
48
59
  import org.jruby.anno.JRubyMethod;
60
+ import org.jruby.runtime.Block;
49
61
  import org.jruby.runtime.ObjectAllocator;
50
62
  import org.jruby.runtime.ThreadContext;
51
63
  import org.jruby.runtime.builtin.IRubyObject;
@@ -58,6 +70,54 @@ import org.jruby.util.ByteList;
58
70
  public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
59
71
  private static final long serialVersionUID = -745716565005219263L;
60
72
 
73
+ private static final int DATETIMEOFFSET_TYPE;
74
+ private static final Method DateTimeOffsetGetMinutesOffsetMethod;
75
+ private static final Method DateTimeOffsetGetTimestampMethod;
76
+ private static final Method DateTimeOffsetValueOfMethod;
77
+ private static final Method PreparedStatementSetDateTimeOffsetMethod;
78
+
79
+ private static final Map<String, Integer> MSSQL_JDBC_TYPE_FOR = new HashMap<String, Integer>(32, 1);
80
+ static {
81
+
82
+ Class<?> DateTimeOffset;
83
+ Class<?> MssqlPreparedStatement;
84
+ Class<?> MssqlTypes;
85
+ try {
86
+ DateTimeOffset = Class.forName("microsoft.sql.DateTimeOffset");
87
+ MssqlPreparedStatement = Class.forName("com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement");
88
+ MssqlTypes = Class.forName("microsoft.sql.Types");
89
+ } catch (ClassNotFoundException e) {
90
+ System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
91
+ throw new RuntimeException("You must require the Microsoft JDBC driver to use this gem");
92
+ }
93
+
94
+ try {
95
+ DATETIMEOFFSET_TYPE = MssqlTypes.getField("DATETIMEOFFSET").getInt(null);
96
+ DateTimeOffsetGetMinutesOffsetMethod = DateTimeOffset.getDeclaredMethod("getMinutesOffset");
97
+ DateTimeOffsetGetTimestampMethod = DateTimeOffset.getDeclaredMethod("getTimestamp");
98
+
99
+ Class<?>[] valueOfArgTypes = { Timestamp.class, int.class };
100
+ DateTimeOffsetValueOfMethod = DateTimeOffset.getDeclaredMethod("valueOf", valueOfArgTypes);
101
+
102
+ Class<?>[] setOffsetArgTypes = { int.class, DateTimeOffset };
103
+ PreparedStatementSetDateTimeOffsetMethod = MssqlPreparedStatement.getDeclaredMethod("setDateTimeOffset", setOffsetArgTypes);
104
+ } catch (Exception e) {
105
+ System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
106
+ throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
107
+ }
108
+
109
+ MSSQL_JDBC_TYPE_FOR.put("binary_basic", Types.BINARY);
110
+ MSSQL_JDBC_TYPE_FOR.put("image", Types.BINARY);
111
+ MSSQL_JDBC_TYPE_FOR.put("datetimeoffset", DATETIMEOFFSET_TYPE);
112
+ MSSQL_JDBC_TYPE_FOR.put("money", Types.DECIMAL);
113
+ MSSQL_JDBC_TYPE_FOR.put("smalldatetime", Types.TIMESTAMP);
114
+ MSSQL_JDBC_TYPE_FOR.put("smallmoney", Types.DECIMAL);
115
+ MSSQL_JDBC_TYPE_FOR.put("ss_timestamp", Types.BINARY);
116
+ MSSQL_JDBC_TYPE_FOR.put("text_basic", Types.LONGVARCHAR);
117
+ MSSQL_JDBC_TYPE_FOR.put("uuid", Types.CHAR);
118
+ MSSQL_JDBC_TYPE_FOR.put("varchar_max", Types.VARCHAR);
119
+ }
120
+
61
121
  public MSSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
62
122
  super(runtime, metaClass);
63
123
  }
@@ -89,6 +149,176 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
89
149
  return context.runtime.newBoolean( startsWithIgnoreCase(sqlBytes, EXEC) );
90
150
  }
91
151
 
152
+ // Support multiple result sets for mssql
153
+ @Override
154
+ @JRubyMethod(name = "execute", required = 1)
155
+ public IRubyObject execute(final ThreadContext context, final IRubyObject sql) {
156
+ final String query = sqlString(sql);
157
+ return withConnection(context, new Callable<IRubyObject>() {
158
+ public IRubyObject call(final Connection connection) throws SQLException {
159
+ Statement statement = null;
160
+ try {
161
+ statement = createStatement(context, connection);
162
+
163
+ // For DBs that do support multiple statements, lets return the last result set
164
+ // to be consistent with AR
165
+ boolean hasResultSet = doExecute(statement, query);
166
+ int updateCount = statement.getUpdateCount();
167
+
168
+ final List<IRubyObject> results = new ArrayList<IRubyObject>();
169
+ ResultSet resultSet;
170
+
171
+ while (hasResultSet || updateCount != -1) {
172
+
173
+ if (hasResultSet) {
174
+ resultSet = statement.getResultSet();
175
+
176
+ // Unfortunately the result set gets closed when getMoreResults()
177
+ // is called, so we have to process the result sets as we get them
178
+ // this shouldn't be an issue in most cases since we're only getting 1 result set anyways
179
+ results.add(mapExecuteResult(context, connection, resultSet));
180
+ } else {
181
+ results.add(context.runtime.newFixnum(updateCount));
182
+ }
183
+
184
+ // Check to see if there is another result set
185
+ hasResultSet = statement.getMoreResults();
186
+ updateCount = statement.getUpdateCount();
187
+ }
188
+
189
+ if (results.size() == 0) {
190
+ return context.nil; // If no results, return nil
191
+ } else if (results.size() == 1) {
192
+ return results.get(0);
193
+ } else {
194
+ return context.runtime.newArray(results);
195
+ }
196
+
197
+ } catch (final SQLException e) {
198
+ debugErrorSQL(context, query);
199
+ throw e;
200
+ } finally {
201
+ close(statement);
202
+ }
203
+ }
204
+ });
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
+
258
+ @Override
259
+ protected Integer jdbcTypeFor(final String type) {
260
+
261
+ Integer typeValue = MSSQL_JDBC_TYPE_FOR.get(type);
262
+
263
+ if ( typeValue != null ) {
264
+ return typeValue;
265
+ }
266
+
267
+ return super.jdbcTypeFor(type);
268
+ }
269
+
270
+ // Datetimeoffset values also make it into here
271
+ @Override
272
+ protected void setStringParameter(final ThreadContext context, final Connection connection,
273
+ final PreparedStatement statement, final int index, final IRubyObject value,
274
+ final IRubyObject attribute, final int type) throws SQLException {
275
+
276
+ // datetimeoffset values also make it in here
277
+ if (type == DATETIMEOFFSET_TYPE) {
278
+
279
+ Object dto = convertToDateTimeOffset(context, value);
280
+
281
+ try {
282
+
283
+ Object[] setStatementArgs = { index, dto };
284
+ PreparedStatementSetDateTimeOffsetMethod.invoke(statement, setStatementArgs);
285
+
286
+ } catch (IllegalAccessException e) {
287
+ debugMessage(context.runtime, e.getMessage());
288
+ throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
289
+ } catch (InvocationTargetException e) {
290
+ debugMessage(context.runtime, e.getMessage());
291
+ throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
292
+ }
293
+
294
+ return;
295
+ }
296
+ super.setStringParameter(context, connection, statement, index, value, attribute, type);
297
+ }
298
+
299
+ private Object convertToDateTimeOffset(final ThreadContext context, final IRubyObject value) {
300
+
301
+ RubyTime time = (RubyTime) value;
302
+ DateTime dt = time.getDateTime();
303
+ Timestamp timestamp = new Timestamp(dt.getMillis());
304
+ timestamp.setNanos(timestamp.getNanos() + (int) time.getNSec());
305
+ int offsetMinutes = dt.getZone().getOffset(dt.getMillis()) / 60000;
306
+
307
+ try {
308
+
309
+ Object[] dtoArgs = { timestamp, offsetMinutes };
310
+ return DateTimeOffsetValueOfMethod.invoke(null, dtoArgs);
311
+
312
+ } catch (IllegalAccessException e) {
313
+ debugMessage(context.runtime, e.getMessage());
314
+ throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
315
+ } catch (InvocationTargetException e) {
316
+ debugMessage(context.runtime, e.getMessage());
317
+ throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
318
+ }
319
+ }
320
+
321
+
92
322
  @Override
93
323
  protected RubyArray mapTables(final ThreadContext context, final Connection connection,
94
324
  final String catalog, final String schemaPattern, final String tablePattern,
@@ -305,29 +535,6 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
305
535
  statement.setObject(index, timeStr, Types.NVARCHAR);
306
536
  }
307
537
 
308
- // Overrides the method in parent, we only remove the savepoint
309
- // from the getSavepoints Map
310
- @JRubyMethod(name = "release_savepoint", required = 1)
311
- public IRubyObject release_savepoint(final ThreadContext context, final IRubyObject name) {
312
- if (name == context.nil) throw context.runtime.newArgumentError("nil savepoint name given");
313
-
314
- final Connection connection = getConnection(true);
315
-
316
- Object savepoint = getSavepoints(context).remove(name);
317
-
318
- if (savepoint == null) throw newSavepointNotSetError(context, name, "release");
319
-
320
- // NOTE: RubyHash.remove does not convert to Java as get does :
321
- if (!(savepoint instanceof Savepoint)) {
322
- savepoint = ((IRubyObject) savepoint).toJava(Savepoint.class);
323
- }
324
-
325
- // The 'releaseSavepoint' method is not currently supported
326
- // by the Microsoft SQL Server JDBC Driver
327
- // connection.releaseSavepoint((Savepoint) savepoint);
328
- return context.nil;
329
- }
330
-
331
538
  //----------------------------------------------------------------
332
539
  // read_uncommitted: "READ UNCOMMITTED",
333
540
  // read_committed: "READ COMMITTED",
@@ -443,6 +650,42 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
443
650
  });
444
651
  }
445
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
+
446
689
  /**
447
690
  * Microsoft SQL 2000+ support schemas
448
691
  */
@@ -453,16 +696,64 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
453
696
 
454
697
  /**
455
698
  * Treat LONGVARCHAR as CLOB on MSSQL for purposes of converting a JDBC value to Ruby.
699
+ * Also handle datetimeoffset values here
456
700
  */
457
701
  @Override
458
702
  protected IRubyObject jdbcToRuby(
459
703
  final ThreadContext context, final Ruby runtime,
460
704
  final int column, int type, final ResultSet resultSet)
461
705
  throws SQLException {
462
- if ( type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR ) type = Types.CLOB;
706
+
707
+ if (type == DATETIMEOFFSET_TYPE) {
708
+
709
+ Object dto = resultSet.getObject(column); // Returns a microsoft.sql.DateTimeOffset
710
+
711
+ if (dto == null) return context.nil;
712
+
713
+ try {
714
+
715
+ int minutes = (int) DateTimeOffsetGetMinutesOffsetMethod.invoke(dto);
716
+ DateTimeZone zone = DateTimeZone.forOffsetHoursMinutes(minutes / 60, minutes % 60);
717
+ Timestamp ts = (Timestamp) DateTimeOffsetGetTimestampMethod.invoke(dto);
718
+
719
+ int nanos = ts.getNanos(); // max 999-999-999
720
+ nanos = nanos % 1000000;
721
+
722
+ // We have to do this differently than the newTime helper because the Timestamp loses its zone information when passed around
723
+ DateTime dateTime = new DateTime(ts.getTime(), zone);
724
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
725
+
726
+ } catch (IllegalAccessException e) {
727
+ debugMessage(runtime, e.getMessage());
728
+ return context.nil;
729
+ } catch (InvocationTargetException e) {
730
+ debugMessage(runtime, e.getMessage());
731
+ return context.nil;
732
+ }
733
+ }
734
+
735
+ if (type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR) type = Types.CLOB;
463
736
  return super.jdbcToRuby(context, runtime, column, type, resultSet);
464
737
  }
465
738
 
739
+ /**
740
+ * Converts a JDBC date object to a Ruby date by referencing Date#civil
741
+ * @param context current thread context
742
+ * @param resultSet the jdbc result set to pull the value from
743
+ * @param index the index of the column to convert
744
+ * @return RubyNil if NULL or RubyDate if there is a value
745
+ * @throws SQLException if it fails to retrieve the value from the result set
746
+ */
747
+ @Override
748
+ protected IRubyObject dateToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
749
+
750
+ final Date value = resultSet.getDate(index);
751
+
752
+ if (value == null) return context.nil;
753
+
754
+ return DateTimeUtils.newDate(context, value);
755
+ }
756
+
466
757
  @Override
467
758
  protected ColumnData[] extractColumns(final ThreadContext context,
468
759
  final Connection connection, final ResultSet resultSet,
@@ -492,19 +783,9 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
492
783
  return columns;
493
784
  }
494
785
 
495
- // internal helper not meant as a "public" API - used in one place thus every
496
- @JRubyMethod(name = "jtds_driver?")
497
- public RubyBoolean jtds_driver_p(final ThreadContext context) throws SQLException {
498
- // "jTDS Type 4 JDBC Driver for MS SQL Server and Sybase"
499
- // SQLJDBC: "Microsoft JDBC Driver 4.0 for SQL Server"
500
- return withConnection(context, new Callable<RubyBoolean>() {
501
- // NOTE: only used in one place for now (on release_savepoint) ...
502
- // might get optimized to only happen once since driver won't change
503
- public RubyBoolean call(final Connection connection) throws SQLException {
504
- final String driver = connection.getMetaData().getDriverName();
505
- return context.getRuntime().newBoolean( driver.indexOf("jTDS") >= 0 );
506
- }
507
- });
786
+ @Override
787
+ protected void releaseSavepoint(final Connection connection, final Savepoint savepoint) throws SQLException {
788
+ // MSSQL doesn't support releasing savepoints
508
789
  }
509
790
 
510
791
  }
@@ -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
  }