activerecord-jdbc-alt-adapter 50.7.0-java → 51.3.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 (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
  //