activerecord-jdbc-alt-adapter 50.7.0-java → 51.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.travis.yml +24 -28
  4. data/Gemfile +5 -2
  5. data/README.md +26 -20
  6. data/Rakefile +4 -30
  7. data/activerecord-jdbc-adapter.gemspec +2 -2
  8. data/activerecord-jdbc-alt-adapter.gemspec +4 -3
  9. data/lib/arel/visitors/sqlserver.rb +17 -3
  10. data/lib/arjdbc/abstract/core.rb +4 -2
  11. data/lib/arjdbc/abstract/database_statements.rb +2 -8
  12. data/lib/arjdbc/abstract/transaction_support.rb +2 -9
  13. data/lib/arjdbc/db2/adapter.rb +2 -52
  14. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  15. data/lib/arjdbc/jdbc/column.rb +11 -5
  16. data/lib/arjdbc/jdbc/error.rb +1 -1
  17. data/lib/arjdbc/jdbc.rb +4 -0
  18. data/lib/arjdbc/mssql/adapter.rb +33 -39
  19. data/lib/arjdbc/mssql/connection_methods.rb +0 -5
  20. data/lib/arjdbc/mssql/database_statements.rb +1 -1
  21. data/lib/arjdbc/mssql/explain_support.rb +1 -1
  22. data/lib/arjdbc/mssql/{extensions.rb → extensions/attribute_methods.rb} +0 -0
  23. data/lib/arjdbc/mssql/extensions/calculations.rb +29 -0
  24. data/lib/arjdbc/mssql/schema_creation.rb +12 -0
  25. data/lib/arjdbc/mssql/schema_definitions.rb +17 -0
  26. data/lib/arjdbc/mssql/schema_dumper.rb +37 -0
  27. data/lib/arjdbc/mssql/schema_statements.rb +73 -44
  28. data/lib/arjdbc/mssql/types/date_and_time_types.rb +9 -9
  29. data/lib/arjdbc/mssql/utils.rb +1 -0
  30. data/lib/arjdbc/mssql.rb +1 -1
  31. data/lib/arjdbc/mysql/adapter.rb +12 -1
  32. data/lib/arjdbc/mysql/connection_methods.rb +7 -13
  33. data/lib/arjdbc/postgresql/adapter.rb +29 -12
  34. data/lib/arjdbc/postgresql/column.rb +3 -6
  35. data/lib/arjdbc/postgresql/connection_methods.rb +1 -3
  36. data/lib/arjdbc/postgresql/oid_types.rb +7 -12
  37. data/lib/arjdbc/sqlite3/adapter.rb +169 -139
  38. data/lib/arjdbc/sqlite3/connection_methods.rb +1 -2
  39. data/lib/arjdbc/version.rb +1 -1
  40. data/rakelib/01-tomcat.rake +2 -2
  41. data/rakelib/02-test.rake +2 -0
  42. data/rakelib/rails.rake +1 -1
  43. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -68
  44. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +38 -282
  45. data/src/java/arjdbc/postgresql/ByteaUtils.java +1 -0
  46. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +3 -3
  47. data/src/java/arjdbc/util/DateTimeUtils.java +5 -22
  48. metadata +20 -12
  49. data/lib/activerecord-jdbc-alt-adapter.rb +0 -1
@@ -487,7 +487,7 @@ public class RubyJdbcConnection extends RubyObject {
487
487
  savepoint = ((IRubyObject) savepoint).toJava(Savepoint.class);
488
488
  }
489
489
 
490
- releaseSavepoint(connection, (Savepoint) savepoint);
490
+ connection.releaseSavepoint((Savepoint) savepoint);
491
491
  return context.nil;
492
492
  }
493
493
  catch (SQLException e) {
@@ -495,11 +495,6 @@ public class RubyJdbcConnection extends RubyObject {
495
495
  }
496
496
  }
497
497
 
498
- // MSSQL doesn't support releasing savepoints so we make it possible to override the actual release action
499
- protected void releaseSavepoint(final Connection connection, final Savepoint savepoint) throws SQLException {
500
- connection.releaseSavepoint(savepoint);
501
- }
502
-
503
498
  protected static RuntimeException newSavepointNotSetError(final ThreadContext context, final IRubyObject name, final String op) {
504
499
  RubyClass StatementInvalid = ActiveRecord(context).getClass("StatementInvalid");
505
500
  return context.runtime.newRaiseException(StatementInvalid, "could not " + op + " savepoint: '" + name + "' (not set)");
@@ -727,10 +722,7 @@ public class RubyJdbcConnection extends RubyObject {
727
722
 
728
723
  private void connectImpl(final boolean forceConnection) throws SQLException {
729
724
  setConnection( forceConnection ? newConnection() : null );
730
- if (forceConnection) {
731
- if (getConnectionImpl() == null) throw new SQLException("Didn't get a connection. Wrong URL?");
732
- configureConnection();
733
- }
725
+ if ( forceConnection ) configureConnection();
734
726
  }
735
727
 
736
728
  @JRubyMethod(name = "read_only?")
@@ -888,31 +880,15 @@ public class RubyJdbcConnection extends RubyObject {
888
880
  return mapQueryResult(context, connection, resultSet);
889
881
  }
890
882
 
891
- private static String[] createStatementPk(IRubyObject pk) {
892
- String[] statementPk;
893
- if (pk instanceof RubyArray) {
894
- RubyArray ary = (RubyArray) pk;
895
- int size = ary.size();
896
- statementPk = new String[size];
897
- for (int i = 0; i < size; i++) {
898
- statementPk[i] = sqlString(ary.eltInternal(i));
899
- }
900
- } else {
901
- statementPk = new String[] { sqlString(pk) };
902
- }
903
- return statementPk;
904
- }
905
-
906
883
  /**
907
884
  * Executes an INSERT SQL statement
908
885
  * @param context
909
886
  * @param sql
910
- * @param pk Rails PK
911
887
  * @return ActiveRecord::Result
912
888
  * @throws SQLException
913
889
  */
914
- @JRubyMethod(name = "execute_insert_pk", required = 2)
915
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
890
+ @JRubyMethod(name = "execute_insert", required = 1)
891
+ public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
916
892
  return withConnection(context, new Callable<IRubyObject>() {
917
893
  public IRubyObject call(final Connection connection) throws SQLException {
918
894
  Statement statement = null;
@@ -920,13 +896,7 @@ public class RubyJdbcConnection extends RubyObject {
920
896
  try {
921
897
 
922
898
  statement = createStatement(context, connection);
923
-
924
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
925
- statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
926
- } else {
927
- statement.executeUpdate(query, createStatementPk(pk));
928
- }
929
-
899
+ statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
930
900
  return mapGeneratedKeys(context, connection, statement);
931
901
 
932
902
  } catch (final SQLException e) {
@@ -939,35 +909,23 @@ public class RubyJdbcConnection extends RubyObject {
939
909
  });
940
910
  }
941
911
 
942
- @Deprecated
943
- @JRubyMethod(name = "execute_insert", required = 1)
944
- public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
945
- return execute_insert_pk(context, sql, context.nil);
946
- }
947
-
948
912
  /**
949
913
  * Executes an INSERT SQL statement using a prepared statement
950
914
  * @param context
951
915
  * @param sql
952
916
  * @param binds RubyArray of values to be bound to the query
953
- * @param pk Rails PK
954
917
  * @return ActiveRecord::Result
955
918
  * @throws SQLException
956
919
  */
957
- @JRubyMethod(name = "execute_insert_pk", required = 3)
958
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
959
- final IRubyObject pk) {
920
+ @JRubyMethod(name = "execute_insert", required = 2)
921
+ public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql, final IRubyObject binds) {
960
922
  return withConnection(context, new Callable<IRubyObject>() {
961
923
  public IRubyObject call(final Connection connection) throws SQLException {
962
924
  PreparedStatement statement = null;
963
925
  final String query = sqlString(sql);
964
926
  try {
965
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
966
- statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
967
- } else {
968
- statement = connection.prepareStatement(query, createStatementPk(pk));
969
- }
970
927
 
928
+ statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
971
929
  setStatementParameters(context, connection, statement, (RubyArray) binds);
972
930
  statement.executeUpdate();
973
931
  return mapGeneratedKeys(context, connection, statement);
@@ -982,12 +940,6 @@ public class RubyJdbcConnection extends RubyObject {
982
940
  });
983
941
  }
984
942
 
985
- @Deprecated
986
- @JRubyMethod(name = "execute_insert", required = 2)
987
- public IRubyObject execute_insert(final ThreadContext context, final IRubyObject binds, final IRubyObject sql) {
988
- return execute_insert_pk(context, sql, binds, context.nil);
989
- }
990
-
991
943
  /**
992
944
  * Executes an UPDATE (DELETE) SQL statement
993
945
  * @param context
@@ -2742,18 +2694,22 @@ public class RubyJdbcConnection extends RubyObject {
2742
2694
 
2743
2695
  final IRubyObject value = value_site.call(context, attribute, attribute);
2744
2696
 
2745
- if ( value instanceof RubyInteger ) {
2697
+ if (value instanceof RubyInteger) {
2746
2698
  return "integer";
2747
2699
  }
2748
2700
 
2749
- if ( value instanceof RubyNumeric ) {
2701
+ if (value instanceof RubyNumeric) {
2750
2702
  return "float";
2751
2703
  }
2752
2704
 
2753
- if ( value instanceof RubyTime ) {
2705
+ if (value instanceof RubyTime) {
2754
2706
  return "timestamp";
2755
2707
  }
2756
2708
 
2709
+ if (value instanceof RubyBoolean) {
2710
+ return "boolean";
2711
+ }
2712
+
2757
2713
  return "string";
2758
2714
  }
2759
2715
 
@@ -2943,9 +2899,8 @@ public class RubyJdbcConnection extends RubyObject {
2943
2899
  value = value.callMethod(context, "to_date");
2944
2900
  }
2945
2901
 
2946
- // NOTE: Here we rely in ActiveRecord (ActiveSupport) to get
2947
- // the date as a string in the database format.
2948
- statement.setDate(index, Date.valueOf(value.callMethod(context, "to_s", context.runtime.newSymbol("db")).toString()));
2902
+ // NOTE: assuming Date#to_s does right ...
2903
+ statement.setDate(index, Date.valueOf(value.toString()));
2949
2904
  }
2950
2905
 
2951
2906
  protected void setBooleanParameter(final ThreadContext context,
@@ -3972,12 +3927,6 @@ public class RubyJdbcConnection extends RubyObject {
3972
3927
  }
3973
3928
  }
3974
3929
 
3975
- public static void debugMessage(final ThreadContext context, final IRubyObject obj) {
3976
- if ( isDebug(context.runtime) ) {
3977
- debugMessage(context.runtime, obj.callMethod(context, "inspect"));
3978
- }
3979
- }
3980
-
3981
3930
  public static void debugMessage(final Ruby runtime, final String msg, final Object e) {
3982
3931
  if ( isDebug(runtime) ) {
3983
3932
  final PrintStream out = runtime != null ? runtime.getOut() : System.out;
@@ -29,33 +29,22 @@ 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;
34
32
  import java.sql.Connection;
35
33
  import java.sql.DatabaseMetaData;
36
- import java.sql.Date;
37
34
  import java.sql.PreparedStatement;
38
35
  import java.sql.ResultSet;
39
36
  import java.sql.Savepoint;
40
- import java.sql.Statement;
41
37
  import java.sql.SQLException;
42
- import java.sql.Timestamp;
43
38
  import java.sql.Types;
39
+ import java.sql.Timestamp;
44
40
  import java.util.Locale;
45
- import java.util.ArrayList;
46
- import java.util.HashMap;
47
- import java.util.List;
48
- import java.util.Map;
49
41
 
50
- import org.joda.time.DateTime;
51
- import org.joda.time.DateTimeZone;
52
42
  import org.jruby.Ruby;
53
43
  import org.jruby.RubyArray;
54
44
  import org.jruby.RubyBoolean;
55
45
  import org.jruby.RubyClass;
56
46
  import org.jruby.RubyString;
57
47
  import org.jruby.RubySymbol;
58
- import org.jruby.RubyTime;
59
48
  import org.jruby.anno.JRubyMethod;
60
49
  import org.jruby.runtime.ObjectAllocator;
61
50
  import org.jruby.runtime.ThreadContext;
@@ -69,54 +58,6 @@ import org.jruby.util.ByteList;
69
58
  public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
70
59
  private static final long serialVersionUID = -745716565005219263L;
71
60
 
72
- private static final int DATETIMEOFFSET_TYPE;
73
- private static final Method DateTimeOffsetGetMinutesOffsetMethod;
74
- private static final Method DateTimeOffsetGetTimestampMethod;
75
- private static final Method DateTimeOffsetValueOfMethod;
76
- private static final Method PreparedStatementSetDateTimeOffsetMethod;
77
-
78
- private static final Map<String, Integer> MSSQL_JDBC_TYPE_FOR = new HashMap<String, Integer>(32, 1);
79
- static {
80
-
81
- Class<?> DateTimeOffset;
82
- Class<?> MssqlPreparedStatement;
83
- Class<?> MssqlTypes;
84
- try {
85
- DateTimeOffset = Class.forName("microsoft.sql.DateTimeOffset");
86
- MssqlPreparedStatement = Class.forName("com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement");
87
- MssqlTypes = Class.forName("microsoft.sql.Types");
88
- } catch (ClassNotFoundException e) {
89
- System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
90
- throw new RuntimeException("You must require the Microsoft JDBC driver to use this gem");
91
- }
92
-
93
- try {
94
- DATETIMEOFFSET_TYPE = MssqlTypes.getField("DATETIMEOFFSET").getInt(null);
95
- DateTimeOffsetGetMinutesOffsetMethod = DateTimeOffset.getDeclaredMethod("getMinutesOffset");
96
- DateTimeOffsetGetTimestampMethod = DateTimeOffset.getDeclaredMethod("getTimestamp");
97
-
98
- Class<?>[] valueOfArgTypes = { Timestamp.class, int.class };
99
- DateTimeOffsetValueOfMethod = DateTimeOffset.getDeclaredMethod("valueOf", valueOfArgTypes);
100
-
101
- Class<?>[] setOffsetArgTypes = { int.class, DateTimeOffset };
102
- PreparedStatementSetDateTimeOffsetMethod = MssqlPreparedStatement.getDeclaredMethod("setDateTimeOffset", setOffsetArgTypes);
103
- } catch (Exception e) {
104
- System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
105
- throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
106
- }
107
-
108
- MSSQL_JDBC_TYPE_FOR.put("binary_basic", Types.BINARY);
109
- MSSQL_JDBC_TYPE_FOR.put("image", Types.BINARY);
110
- MSSQL_JDBC_TYPE_FOR.put("datetimeoffset", DATETIMEOFFSET_TYPE);
111
- MSSQL_JDBC_TYPE_FOR.put("money", Types.DECIMAL);
112
- MSSQL_JDBC_TYPE_FOR.put("smalldatetime", Types.TIMESTAMP);
113
- MSSQL_JDBC_TYPE_FOR.put("smallmoney", Types.DECIMAL);
114
- MSSQL_JDBC_TYPE_FOR.put("ss_timestamp", Types.BINARY);
115
- MSSQL_JDBC_TYPE_FOR.put("text_basic", Types.LONGVARCHAR);
116
- MSSQL_JDBC_TYPE_FOR.put("uuid", Types.CHAR);
117
- MSSQL_JDBC_TYPE_FOR.put("varchar_max", Types.VARCHAR);
118
- }
119
-
120
61
  public MSSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
121
62
  super(runtime, metaClass);
122
63
  }
@@ -148,176 +89,6 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
148
89
  return context.runtime.newBoolean( startsWithIgnoreCase(sqlBytes, EXEC) );
149
90
  }
150
91
 
151
- // Support multiple result sets for mssql
152
- @Override
153
- @JRubyMethod(name = "execute", required = 1)
154
- public IRubyObject execute(final ThreadContext context, final IRubyObject sql) {
155
- final String query = sqlString(sql);
156
- return withConnection(context, new Callable<IRubyObject>() {
157
- public IRubyObject call(final Connection connection) throws SQLException {
158
- Statement statement = null;
159
- try {
160
- statement = createStatement(context, connection);
161
-
162
- // For DBs that do support multiple statements, lets return the last result set
163
- // to be consistent with AR
164
- boolean hasResultSet = doExecute(statement, query);
165
- int updateCount = statement.getUpdateCount();
166
-
167
- final List<IRubyObject> results = new ArrayList<IRubyObject>();
168
- ResultSet resultSet;
169
-
170
- while (hasResultSet || updateCount != -1) {
171
-
172
- if (hasResultSet) {
173
- resultSet = statement.getResultSet();
174
-
175
- // Unfortunately the result set gets closed when getMoreResults()
176
- // is called, so we have to process the result sets as we get them
177
- // this shouldn't be an issue in most cases since we're only getting 1 result set anyways
178
- results.add(mapExecuteResult(context, connection, resultSet));
179
- } else {
180
- results.add(context.runtime.newFixnum(updateCount));
181
- }
182
-
183
- // Check to see if there is another result set
184
- hasResultSet = statement.getMoreResults();
185
- updateCount = statement.getUpdateCount();
186
- }
187
-
188
- if (results.size() == 0) {
189
- return context.nil; // If no results, return nil
190
- } else if (results.size() == 1) {
191
- return results.get(0);
192
- } else {
193
- return context.runtime.newArray(results);
194
- }
195
-
196
- } catch (final SQLException e) {
197
- debugErrorSQL(context, query);
198
- throw e;
199
- } finally {
200
- close(statement);
201
- }
202
- }
203
- });
204
- }
205
-
206
- /**
207
- * Executes an INSERT SQL statement
208
- * @param context
209
- * @param sql
210
- * @param pk Rails PK
211
- * @return ActiveRecord::Result
212
- * @throws SQLException
213
- */
214
- @Override
215
- @JRubyMethod(name = "execute_insert_pk", required = 2)
216
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
217
-
218
- // MSSQL does not like composite primary keys here so chop it if there is more than one column
219
- IRubyObject modifiedPk = pk;
220
-
221
- if (pk instanceof RubyArray) {
222
- RubyArray ary = (RubyArray) pk;
223
- if (ary.size() > 0) {
224
- modifiedPk = ary.eltInternal(0);
225
- }
226
- }
227
-
228
- return super.execute_insert_pk(context, sql, modifiedPk);
229
- }
230
-
231
- /**
232
- * Executes an INSERT SQL statement using a prepared statement
233
- * @param context
234
- * @param sql
235
- * @param binds RubyArray of values to be bound to the query
236
- * @param pk Rails PK
237
- * @return ActiveRecord::Result
238
- * @throws SQLException
239
- */
240
- @Override
241
- @JRubyMethod(name = "execute_insert_pk", required = 3)
242
- public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
243
- final IRubyObject pk) {
244
- // MSSQL does not like composite primary keys here so chop it if there is more than one column
245
- IRubyObject modifiedPk = pk;
246
-
247
- if (pk instanceof RubyArray) {
248
- RubyArray ary = (RubyArray) pk;
249
- if (ary.size() > 0) {
250
- modifiedPk = ary.eltInternal(0);
251
- }
252
- }
253
-
254
- return super.execute_insert_pk(context, sql, binds, modifiedPk);
255
- }
256
-
257
- @Override
258
- protected Integer jdbcTypeFor(final String type) {
259
-
260
- Integer typeValue = MSSQL_JDBC_TYPE_FOR.get(type);
261
-
262
- if ( typeValue != null ) {
263
- return typeValue;
264
- }
265
-
266
- return super.jdbcTypeFor(type);
267
- }
268
-
269
- // Datetimeoffset values also make it into here
270
- @Override
271
- protected void setStringParameter(final ThreadContext context, final Connection connection,
272
- final PreparedStatement statement, final int index, final IRubyObject value,
273
- final IRubyObject attribute, final int type) throws SQLException {
274
-
275
- // datetimeoffset values also make it in here
276
- if (type == DATETIMEOFFSET_TYPE) {
277
-
278
- Object dto = convertToDateTimeOffset(context, value);
279
-
280
- try {
281
-
282
- Object[] setStatementArgs = { index, dto };
283
- PreparedStatementSetDateTimeOffsetMethod.invoke(statement, setStatementArgs);
284
-
285
- } catch (IllegalAccessException e) {
286
- debugMessage(context.runtime, e.getMessage());
287
- throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
288
- } catch (InvocationTargetException e) {
289
- debugMessage(context.runtime, e.getMessage());
290
- throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
291
- }
292
-
293
- return;
294
- }
295
- super.setStringParameter(context, connection, statement, index, value, attribute, type);
296
- }
297
-
298
- private Object convertToDateTimeOffset(final ThreadContext context, final IRubyObject value) {
299
-
300
- RubyTime time = (RubyTime) value;
301
- DateTime dt = time.getDateTime();
302
- Timestamp timestamp = new Timestamp(dt.getMillis());
303
- timestamp.setNanos(timestamp.getNanos() + (int) time.getNSec());
304
- int offsetMinutes = dt.getZone().getOffset(dt.getMillis()) / 60000;
305
-
306
- try {
307
-
308
- Object[] dtoArgs = { timestamp, offsetMinutes };
309
- return DateTimeOffsetValueOfMethod.invoke(null, dtoArgs);
310
-
311
- } catch (IllegalAccessException e) {
312
- debugMessage(context.runtime, e.getMessage());
313
- throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
314
- } catch (InvocationTargetException e) {
315
- debugMessage(context.runtime, e.getMessage());
316
- throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
317
- }
318
- }
319
-
320
-
321
92
  @Override
322
93
  protected RubyArray mapTables(final ThreadContext context, final Connection connection,
323
94
  final String catalog, final String schemaPattern, final String tablePattern,
@@ -534,6 +305,29 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
534
305
  statement.setObject(index, timeStr, Types.NVARCHAR);
535
306
  }
536
307
 
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
+
537
331
  //----------------------------------------------------------------
538
332
  // read_uncommitted: "READ UNCOMMITTED",
539
333
  // read_committed: "READ COMMITTED",
@@ -659,64 +453,16 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
659
453
 
660
454
  /**
661
455
  * Treat LONGVARCHAR as CLOB on MSSQL for purposes of converting a JDBC value to Ruby.
662
- * Also handle datetimeoffset values here
663
456
  */
664
457
  @Override
665
458
  protected IRubyObject jdbcToRuby(
666
459
  final ThreadContext context, final Ruby runtime,
667
460
  final int column, int type, final ResultSet resultSet)
668
461
  throws SQLException {
669
-
670
- if (type == DATETIMEOFFSET_TYPE) {
671
-
672
- Object dto = resultSet.getObject(column); // Returns a microsoft.sql.DateTimeOffset
673
-
674
- if (dto == null) return context.nil;
675
-
676
- try {
677
-
678
- int minutes = (int) DateTimeOffsetGetMinutesOffsetMethod.invoke(dto);
679
- DateTimeZone zone = DateTimeZone.forOffsetHoursMinutes(minutes / 60, minutes % 60);
680
- Timestamp ts = (Timestamp) DateTimeOffsetGetTimestampMethod.invoke(dto);
681
-
682
- int nanos = ts.getNanos(); // max 999-999-999
683
- nanos = nanos % 1000000;
684
-
685
- // We have to do this differently than the newTime helper because the Timestamp loses its zone information when passed around
686
- DateTime dateTime = new DateTime(ts.getTime(), zone);
687
- return RubyTime.newTime(context.runtime, dateTime, nanos);
688
-
689
- } catch (IllegalAccessException e) {
690
- debugMessage(runtime, e.getMessage());
691
- return context.nil;
692
- } catch (InvocationTargetException e) {
693
- debugMessage(runtime, e.getMessage());
694
- return context.nil;
695
- }
696
- }
697
-
698
- if (type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR) type = Types.CLOB;
462
+ if ( type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR ) type = Types.CLOB;
699
463
  return super.jdbcToRuby(context, runtime, column, type, resultSet);
700
464
  }
701
465
 
702
- /**
703
- * Converts a JDBC date object to a Ruby date by referencing Date#civil
704
- * @param context current thread context
705
- * @param resultSet the jdbc result set to pull the value from
706
- * @param index the index of the column to convert
707
- * @return RubyNil if NULL or RubyDate if there is a value
708
- * @throws SQLException if it fails to retrieve the value from the result set
709
- */
710
- @Override
711
- protected IRubyObject dateToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
712
-
713
- final Date value = resultSet.getDate(index);
714
-
715
- if (value == null) return context.nil;
716
-
717
- return DateTimeUtils.newDate(context, value);
718
- }
719
-
720
466
  @Override
721
467
  protected ColumnData[] extractColumns(final ThreadContext context,
722
468
  final Connection connection, final ResultSet resultSet,
@@ -746,9 +492,19 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
746
492
  return columns;
747
493
  }
748
494
 
749
- @Override
750
- protected void releaseSavepoint(final Connection connection, final Savepoint savepoint) throws SQLException {
751
- // MSSQL doesn't support releasing savepoints
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
+ });
752
508
  }
753
509
 
754
510
  }
@@ -88,6 +88,7 @@ abstract class ByteaUtils {
88
88
  }
89
89
  else {
90
90
  correctSize -= 3;
91
+ i += 2;
91
92
  }
92
93
  }
93
94
  }
@@ -27,6 +27,7 @@ package arjdbc.postgresql;
27
27
 
28
28
  import arjdbc.jdbc.Callable;
29
29
  import arjdbc.jdbc.DriverWrapper;
30
+ import arjdbc.postgresql.PostgreSQLResult;
30
31
  import arjdbc.util.DateTimeUtils;
31
32
  import arjdbc.util.StringHelper;
32
33
 
@@ -366,9 +367,8 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
366
367
  value = value.callMethod(context, "to_date");
367
368
  }
368
369
 
369
- // NOTE: Here we rely in ActiveRecord (ActiveSupport) to get
370
- // the date as a string in the database format.
371
- statement.setDate(index, Date.valueOf(value.callMethod(context, "to_s", context.runtime.newSymbol("db")).toString()));
370
+ // NOTE: assuming Date#to_s does right ...
371
+ statement.setDate(index, Date.valueOf(value.toString()));
372
372
  }
373
373
 
374
374
  @Override
@@ -31,7 +31,6 @@ import java.util.TimeZone;
31
31
  import org.joda.time.Chronology;
32
32
  import org.joda.time.DateTime;
33
33
  import org.joda.time.DateTimeZone;
34
- import org.joda.time.chrono.GJChronology;
35
34
  import org.joda.time.chrono.ISOChronology;
36
35
  import org.jruby.Ruby;
37
36
  import org.jruby.RubyFloat;
@@ -51,9 +50,6 @@ import static arjdbc.util.StringHelper.decByte;
51
50
  * @author kares
52
51
  */
53
52
  public abstract class DateTimeUtils {
54
-
55
- private static final GJChronology CHRONO_ITALY_UTC = GJChronology.getInstance(DateTimeZone.UTC);
56
-
57
53
  public static RubyTime toTime(final ThreadContext context, final IRubyObject value) {
58
54
  if (!(value instanceof RubyTime)) { // unlikely
59
55
  return (RubyTime) TypeConverter.convertToTypeWithCheck(value, context.runtime.getTime(), "to_time");
@@ -221,11 +217,9 @@ public abstract class DateTimeUtils {
221
217
  final int hours = time.getHours();
222
218
  final int minutes = time.getMinutes();
223
219
  final int seconds = time.getSeconds();
224
- int nanos = time.getNanos(); // max 999-999-999
225
- final int millis = nanos / 1000000;
226
- nanos = nanos % 1000000;
220
+ final int nanos = time.getNanos(); // max 999-999-999
227
221
 
228
- DateTime dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, millis, defaultZone);
222
+ DateTime dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, defaultZone);
229
223
  return RubyTime.newTime(context.runtime, dateTime, nanos);
230
224
  }
231
225
 
@@ -238,11 +232,9 @@ public abstract class DateTimeUtils {
238
232
  final int hours = timestamp.getHours();
239
233
  final int minutes = timestamp.getMinutes();
240
234
  final int seconds = timestamp.getSeconds();
241
- int nanos = timestamp.getNanos(); // max 999-999-999
242
- final int millis = nanos / 1000000;
243
- nanos = nanos % 1000000;
235
+ final int nanos = timestamp.getNanos(); // max 999-999-999
244
236
 
245
- DateTime dateTime = new DateTime(year, month, day, hours, minutes, seconds, millis, defaultZone);
237
+ DateTime dateTime = new DateTime(year, month, day, hours, minutes, seconds, 0, defaultZone);
246
238
  return RubyTime.newTime(context.runtime, dateTime, nanos);
247
239
  }
248
240
 
@@ -267,15 +259,6 @@ public abstract class DateTimeUtils {
267
259
  return newDate(context, year, month, day, ISOChronology.getInstance(zone));
268
260
  }
269
261
 
270
- @SuppressWarnings("deprecation")
271
- public static IRubyObject newDate(final ThreadContext context, final Date date) {
272
- final int year = date.getYear() + 1900;
273
- final int month = date.getMonth() + 1;
274
- final int day = date.getDate();
275
-
276
- return newDate(context, year, month, day, CHRONO_ITALY_UTC);
277
- }
278
-
279
262
  // @Deprecated
280
263
  public static Timestamp convertToTimestamp(final RubyFloat value) {
281
264
  final Timestamp timestamp = new Timestamp(value.getLongValue() * 1000); // millis
@@ -570,7 +553,7 @@ public abstract class DateTimeUtils {
570
553
  }
571
554
 
572
555
  private static IRubyObject newDate(final ThreadContext context, final int year, final int month, final int day,
573
- final Chronology chronology) {
556
+ final ISOChronology chronology) {
574
557
  // NOTE: JRuby really needs a native date.rb until than its a bit costly going from ...
575
558
  // java.sql.Date -> allocating a DateTime proxy, help a bit by shooting at the internals
576
559
  //