activerecord-jdbc-adapter 51.8-java → 52.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/.travis.yml +26 -51
- data/README.md +9 -11
- data/Rakefile +19 -74
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/arjdbc/abstract/core.rb +2 -12
- data/lib/arjdbc/abstract/database_statements.rb +24 -10
- data/lib/arjdbc/abstract/statement_cache.rb +4 -4
- data/lib/arjdbc/db2/adapter.rb +52 -2
- data/lib/arjdbc/jdbc.rb +4 -0
- data/lib/arjdbc/jdbc/column.rb +11 -5
- data/lib/arjdbc/jdbc/connection_methods.rb +9 -2
- data/lib/arjdbc/jdbc/jdbc.rake +4 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +804 -0
- data/lib/arjdbc/mssql/column.rb +200 -0
- data/lib/arjdbc/mssql/connection_methods.rb +79 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql/adapter.rb +22 -14
- data/lib/arjdbc/mysql/connection_methods.rb +9 -18
- data/lib/arjdbc/postgresql/adapter.rb +102 -75
- data/lib/arjdbc/postgresql/column.rb +3 -6
- data/lib/arjdbc/postgresql/connection_methods.rb +3 -12
- data/lib/arjdbc/postgresql/oid_types.rb +12 -86
- data/lib/arjdbc/sqlite3/adapter.rb +88 -92
- data/lib/arjdbc/sqlite3/connection_methods.rb +0 -1
- data/lib/arjdbc/tasks/database_tasks.rb +36 -16
- data/lib/arjdbc/tasks/databases.rake +75 -32
- data/lib/arjdbc/tasks/databases3.rake +215 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/02-test.rake +3 -0
- data/rakelib/compile.rake +70 -0
- data/rakelib/db.rake +7 -21
- data/rakelib/rails.rake +4 -5
- data/src/java/arjdbc/ArJdbcModule.java +15 -5
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +87 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +1 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +29 -113
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +14 -310
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +13 -21
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +50 -44
- data/src/java/arjdbc/util/DateTimeUtils.java +5 -141
- data/src/java/arjdbc/util/QuotingUtils.java +7 -6
- metadata +26 -11
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +0 -61
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +0 -52
@@ -28,32 +28,17 @@ package arjdbc.mssql;
|
|
28
28
|
import arjdbc.jdbc.Callable;
|
29
29
|
import arjdbc.jdbc.RubyJdbcConnection;
|
30
30
|
|
31
|
-
import java.lang.reflect.InvocationTargetException;
|
32
|
-
import java.lang.reflect.Method;
|
33
31
|
import java.sql.Connection;
|
34
32
|
import java.sql.DatabaseMetaData;
|
35
|
-
import java.sql.Date;
|
36
|
-
import java.sql.PreparedStatement;
|
37
33
|
import java.sql.ResultSet;
|
38
|
-
import java.sql.Savepoint;
|
39
|
-
import java.sql.Statement;
|
40
34
|
import java.sql.SQLException;
|
41
|
-
import java.sql.Timestamp;
|
42
35
|
import java.sql.Types;
|
43
|
-
import java.util.ArrayList;
|
44
|
-
import java.util.HashMap;
|
45
|
-
import java.util.List;
|
46
|
-
import java.util.Map;
|
47
36
|
|
48
|
-
import arjdbc.util.DateTimeUtils;
|
49
|
-
import org.joda.time.DateTime;
|
50
|
-
import org.joda.time.DateTimeZone;
|
51
37
|
import org.jruby.Ruby;
|
52
38
|
import org.jruby.RubyArray;
|
53
39
|
import org.jruby.RubyBoolean;
|
54
40
|
import org.jruby.RubyClass;
|
55
41
|
import org.jruby.RubyString;
|
56
|
-
import org.jruby.RubyTime;
|
57
42
|
import org.jruby.anno.JRubyMethod;
|
58
43
|
import org.jruby.runtime.ObjectAllocator;
|
59
44
|
import org.jruby.runtime.ThreadContext;
|
@@ -67,53 +52,6 @@ import org.jruby.util.ByteList;
|
|
67
52
|
public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
68
53
|
private static final long serialVersionUID = -745716565005219263L;
|
69
54
|
|
70
|
-
private static final int DATETIMEOFFSET_TYPE;
|
71
|
-
private static final Method DateTimeOffsetGetMinutesOffsetMethod;
|
72
|
-
private static final Method DateTimeOffsetGetTimestampMethod;
|
73
|
-
private static final Method DateTimeOffsetValueOfMethod;
|
74
|
-
private static final Method PreparedStatementSetDateTimeOffsetMethod;
|
75
|
-
|
76
|
-
private static final Map<String, Integer> MSSQL_JDBC_TYPE_FOR = new HashMap<String, Integer>(32, 1);
|
77
|
-
static {
|
78
|
-
|
79
|
-
Class<?> DateTimeOffset;
|
80
|
-
Class<?> MssqlPreparedStatement;
|
81
|
-
Class<?> MssqlTypes;
|
82
|
-
try {
|
83
|
-
DateTimeOffset = Class.forName("microsoft.sql.DateTimeOffset");
|
84
|
-
MssqlPreparedStatement = Class.forName("com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement");
|
85
|
-
MssqlTypes = Class.forName("microsoft.sql.Types");
|
86
|
-
} catch (ClassNotFoundException e) {
|
87
|
-
System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
|
88
|
-
throw new RuntimeException("You must require the Microsoft JDBC driver to use this gem");
|
89
|
-
}
|
90
|
-
|
91
|
-
try {
|
92
|
-
DATETIMEOFFSET_TYPE = MssqlTypes.getField("DATETIMEOFFSET").getInt(null);
|
93
|
-
DateTimeOffsetGetMinutesOffsetMethod = DateTimeOffset.getDeclaredMethod("getMinutesOffset");
|
94
|
-
DateTimeOffsetGetTimestampMethod = DateTimeOffset.getDeclaredMethod("getTimestamp");
|
95
|
-
|
96
|
-
Class<?>[] valueOfArgTypes = { Timestamp.class, int.class };
|
97
|
-
DateTimeOffsetValueOfMethod = DateTimeOffset.getDeclaredMethod("valueOf", valueOfArgTypes);
|
98
|
-
|
99
|
-
Class<?>[] setOffsetArgTypes = { int.class, DateTimeOffset };
|
100
|
-
PreparedStatementSetDateTimeOffsetMethod = MssqlPreparedStatement.getDeclaredMethod("setDateTimeOffset", setOffsetArgTypes);
|
101
|
-
} catch (Exception e) {
|
102
|
-
System.err.println("You must require the Microsoft JDBC driver to use this gem"); // The exception doesn't bubble when ruby is initializing
|
103
|
-
throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
|
104
|
-
}
|
105
|
-
|
106
|
-
MSSQL_JDBC_TYPE_FOR.put("binary_basic", Types.BINARY);
|
107
|
-
MSSQL_JDBC_TYPE_FOR.put("datetimeoffset", DATETIMEOFFSET_TYPE);
|
108
|
-
MSSQL_JDBC_TYPE_FOR.put("money", Types.DECIMAL);
|
109
|
-
MSSQL_JDBC_TYPE_FOR.put("smalldatetime", Types.TIMESTAMP);
|
110
|
-
MSSQL_JDBC_TYPE_FOR.put("smallmoney", Types.DECIMAL);
|
111
|
-
MSSQL_JDBC_TYPE_FOR.put("ss_timestamp", Types.BINARY);
|
112
|
-
MSSQL_JDBC_TYPE_FOR.put("text_basic", Types.LONGVARCHAR);
|
113
|
-
MSSQL_JDBC_TYPE_FOR.put("uuid", Types.CHAR);
|
114
|
-
MSSQL_JDBC_TYPE_FOR.put("varchar_max", Types.VARCHAR);
|
115
|
-
}
|
116
|
-
|
117
55
|
public MSSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
118
56
|
super(runtime, metaClass);
|
119
57
|
}
|
@@ -145,187 +83,6 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
145
83
|
return context.runtime.newBoolean( startsWithIgnoreCase(sqlBytes, EXEC) );
|
146
84
|
}
|
147
85
|
|
148
|
-
// Support multiple result sets for mssql
|
149
|
-
@Override
|
150
|
-
@JRubyMethod(name = "execute", required = 1)
|
151
|
-
public IRubyObject execute(final ThreadContext context, final IRubyObject sql) {
|
152
|
-
final String query = sqlString(sql);
|
153
|
-
return withConnection(context, new Callable<IRubyObject>() {
|
154
|
-
public IRubyObject call(final Connection connection) throws SQLException {
|
155
|
-
Statement statement = null;
|
156
|
-
try {
|
157
|
-
statement = createStatement(context, connection);
|
158
|
-
|
159
|
-
// For DBs that do support multiple statements, lets return the last result set
|
160
|
-
// to be consistent with AR
|
161
|
-
boolean hasResultSet = doExecute(statement, query);
|
162
|
-
int updateCount = statement.getUpdateCount();
|
163
|
-
|
164
|
-
final List<IRubyObject> results = new ArrayList<IRubyObject>();
|
165
|
-
ResultSet resultSet;
|
166
|
-
|
167
|
-
while (hasResultSet || updateCount != -1) {
|
168
|
-
|
169
|
-
if (hasResultSet) {
|
170
|
-
resultSet = statement.getResultSet();
|
171
|
-
|
172
|
-
// Unfortunately the result set gets closed when getMoreResults()
|
173
|
-
// is called, so we have to process the result sets as we get them
|
174
|
-
// this shouldn't be an issue in most cases since we're only getting 1 result set anyways
|
175
|
-
results.add(mapExecuteResult(context, connection, resultSet));
|
176
|
-
} else {
|
177
|
-
results.add(context.runtime.newFixnum(updateCount));
|
178
|
-
}
|
179
|
-
|
180
|
-
// Check to see if there is another result set
|
181
|
-
hasResultSet = statement.getMoreResults();
|
182
|
-
updateCount = statement.getUpdateCount();
|
183
|
-
}
|
184
|
-
|
185
|
-
if (results.size() == 0) {
|
186
|
-
return context.nil; // If no results, return nil
|
187
|
-
} else if (results.size() == 1) {
|
188
|
-
return results.get(0);
|
189
|
-
} else {
|
190
|
-
return context.runtime.newArray(results);
|
191
|
-
}
|
192
|
-
|
193
|
-
} catch (final SQLException e) {
|
194
|
-
debugErrorSQL(context, query);
|
195
|
-
throw e;
|
196
|
-
} finally {
|
197
|
-
close(statement);
|
198
|
-
}
|
199
|
-
}
|
200
|
-
});
|
201
|
-
}
|
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
|
-
@Override
|
255
|
-
protected Integer jdbcTypeFor(final String type) {
|
256
|
-
|
257
|
-
Integer typeValue = MSSQL_JDBC_TYPE_FOR.get(type);
|
258
|
-
|
259
|
-
if ( typeValue != null ) {
|
260
|
-
return typeValue;
|
261
|
-
}
|
262
|
-
|
263
|
-
return super.jdbcTypeFor(type);
|
264
|
-
}
|
265
|
-
|
266
|
-
// Datetimeoffset values also make it into here
|
267
|
-
@Override
|
268
|
-
protected void setStringParameter(final ThreadContext context, final Connection connection,
|
269
|
-
final PreparedStatement statement, final int index, final IRubyObject value,
|
270
|
-
final IRubyObject attribute, final int type) throws SQLException {
|
271
|
-
|
272
|
-
// datetimeoffset values also make it in here
|
273
|
-
if (type == DATETIMEOFFSET_TYPE) {
|
274
|
-
|
275
|
-
Object dto = convertToDateTimeOffset(context, value);
|
276
|
-
|
277
|
-
try {
|
278
|
-
|
279
|
-
Object[] setStatementArgs = { index, dto };
|
280
|
-
PreparedStatementSetDateTimeOffsetMethod.invoke(statement, setStatementArgs);
|
281
|
-
|
282
|
-
} catch (IllegalAccessException e) {
|
283
|
-
debugMessage(context.runtime, e.getMessage());
|
284
|
-
throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
|
285
|
-
} catch (InvocationTargetException 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
|
-
}
|
289
|
-
|
290
|
-
return;
|
291
|
-
}
|
292
|
-
super.setStringParameter(context, connection, statement, index, value, attribute, type);
|
293
|
-
}
|
294
|
-
|
295
|
-
// We need higher precision than the default for Time objects (which is milliseconds) so we use a DateTimeOffset object
|
296
|
-
@Override
|
297
|
-
protected void setTimeParameter(final ThreadContext context,
|
298
|
-
final Connection connection, final PreparedStatement statement,
|
299
|
-
final int index, IRubyObject value,
|
300
|
-
final IRubyObject attribute, final int type) throws SQLException {
|
301
|
-
|
302
|
-
statement.setObject(index, convertToDateTimeOffset(context, value), Types.TIME);
|
303
|
-
|
304
|
-
}
|
305
|
-
|
306
|
-
private Object convertToDateTimeOffset(final ThreadContext context, final IRubyObject value) {
|
307
|
-
|
308
|
-
RubyTime time = (RubyTime) value;
|
309
|
-
DateTime dt = time.getDateTime();
|
310
|
-
Timestamp timestamp = new Timestamp(dt.getMillis());
|
311
|
-
timestamp.setNanos(timestamp.getNanos() + (int) time.getNSec());
|
312
|
-
int offsetMinutes = dt.getZone().getOffset(dt.getMillis()) / 60000;
|
313
|
-
|
314
|
-
try {
|
315
|
-
|
316
|
-
Object[] dtoArgs = { timestamp, offsetMinutes };
|
317
|
-
return DateTimeOffsetValueOfMethod.invoke(null, dtoArgs);
|
318
|
-
|
319
|
-
} catch (IllegalAccessException e) {
|
320
|
-
debugMessage(context.runtime, e.getMessage());
|
321
|
-
throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
|
322
|
-
} catch (InvocationTargetException e) {
|
323
|
-
debugMessage(context.runtime, e.getMessage());
|
324
|
-
throw new RuntimeException("Please make sure you are using the latest version of the Microsoft JDBC driver");
|
325
|
-
}
|
326
|
-
}
|
327
|
-
|
328
|
-
|
329
86
|
@Override
|
330
87
|
protected RubyArray mapTables(final ThreadContext context, final Connection connection,
|
331
88
|
final String catalog, final String schemaPattern, final String tablePattern,
|
@@ -367,79 +124,16 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
367
124
|
|
368
125
|
/**
|
369
126
|
* Treat LONGVARCHAR as CLOB on MSSQL for purposes of converting a JDBC value to Ruby.
|
370
|
-
* Also handle datetimeoffset values here
|
371
127
|
*/
|
372
128
|
@Override
|
373
129
|
protected IRubyObject jdbcToRuby(
|
374
130
|
final ThreadContext context, final Ruby runtime,
|
375
131
|
final int column, int type, final ResultSet resultSet)
|
376
132
|
throws SQLException {
|
377
|
-
|
378
|
-
if (type == DATETIMEOFFSET_TYPE) {
|
379
|
-
|
380
|
-
Object dto = resultSet.getObject(column); // Returns a microsoft.sql.DateTimeOffset
|
381
|
-
|
382
|
-
if (dto == null) return context.nil;
|
383
|
-
|
384
|
-
try {
|
385
|
-
|
386
|
-
int minutes = (int) DateTimeOffsetGetMinutesOffsetMethod.invoke(dto);
|
387
|
-
DateTimeZone zone = DateTimeZone.forOffsetHoursMinutes(minutes / 60, minutes % 60);
|
388
|
-
Timestamp ts = (Timestamp) DateTimeOffsetGetTimestampMethod.invoke(dto);
|
389
|
-
|
390
|
-
int nanos = ts.getNanos(); // max 999-999-999
|
391
|
-
nanos = nanos % 1000000;
|
392
|
-
|
393
|
-
// We have to do this differently than the newTime helper because the Timestamp loses its zone information when passed around
|
394
|
-
DateTime dateTime = new DateTime(ts.getTime(), zone);
|
395
|
-
return RubyTime.newTime(context.runtime, dateTime, nanos);
|
396
|
-
|
397
|
-
} catch (IllegalAccessException e) {
|
398
|
-
debugMessage(runtime, e.getMessage());
|
399
|
-
return context.nil;
|
400
|
-
} catch (InvocationTargetException e) {
|
401
|
-
debugMessage(runtime, e.getMessage());
|
402
|
-
return context.nil;
|
403
|
-
}
|
404
|
-
}
|
405
|
-
|
406
|
-
if (type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR) type = Types.CLOB;
|
133
|
+
if ( type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR ) type = Types.CLOB;
|
407
134
|
return super.jdbcToRuby(context, runtime, column, type, resultSet);
|
408
135
|
}
|
409
136
|
|
410
|
-
/**
|
411
|
-
* Converts a JDBC date object to a Ruby date by referencing Date#civil
|
412
|
-
* @param context current thread context
|
413
|
-
* @param resultSet the jdbc result set to pull the value from
|
414
|
-
* @param index the index of the column to convert
|
415
|
-
* @return RubyNil if NULL or RubyDate if there is a value
|
416
|
-
* @throws SQLException if it fails to retrieve the value from the result set
|
417
|
-
*/
|
418
|
-
@Override
|
419
|
-
protected IRubyObject dateToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
|
420
|
-
|
421
|
-
final Date value = resultSet.getDate(index);
|
422
|
-
|
423
|
-
if (value == null) return context.nil;
|
424
|
-
|
425
|
-
return DateTimeUtils.newDate(context, value);
|
426
|
-
}
|
427
|
-
|
428
|
-
/**
|
429
|
-
* Converts a JDBC time to a Ruby time. We use timestamp because java.sql.Time doesn't support sub-millisecond values
|
430
|
-
* @param context current thread context
|
431
|
-
* @param resultSet the jdbc result set to pull the value from
|
432
|
-
* @param index the index of the column to convert
|
433
|
-
* @return RubyNil if NULL or RubyTime if there is a value
|
434
|
-
* @throws SQLException if it fails to retrieve the value from the result set
|
435
|
-
*/
|
436
|
-
@Override
|
437
|
-
protected IRubyObject timeToRuby(final ThreadContext context,final Ruby runtime,
|
438
|
-
final ResultSet resultSet, final int column) throws SQLException {
|
439
|
-
|
440
|
-
return timestampToRuby(context, runtime, resultSet, column);
|
441
|
-
}
|
442
|
-
|
443
137
|
@Override
|
444
138
|
protected ColumnData[] extractColumns(final ThreadContext context,
|
445
139
|
final Connection connection, final ResultSet resultSet,
|
@@ -469,9 +163,19 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
469
163
|
return columns;
|
470
164
|
}
|
471
165
|
|
472
|
-
|
473
|
-
|
474
|
-
|
166
|
+
// internal helper not meant as a "public" API - used in one place thus every
|
167
|
+
@JRubyMethod(name = "jtds_driver?")
|
168
|
+
public RubyBoolean jtds_driver_p(final ThreadContext context) throws SQLException {
|
169
|
+
// "jTDS Type 4 JDBC Driver for MS SQL Server and Sybase"
|
170
|
+
// SQLJDBC: "Microsoft JDBC Driver 4.0 for SQL Server"
|
171
|
+
return withConnection(context, new Callable<RubyBoolean>() {
|
172
|
+
// NOTE: only used in one place for now (on release_savepoint) ...
|
173
|
+
// might get optimized to only happen once since driver won't change
|
174
|
+
public RubyBoolean call(final Connection connection) throws SQLException {
|
175
|
+
final String driver = connection.getMetaData().getDriverName();
|
176
|
+
return context.getRuntime().newBoolean( driver.indexOf("jTDS") >= 0 );
|
177
|
+
}
|
178
|
+
});
|
475
179
|
}
|
476
180
|
|
477
181
|
}
|
@@ -107,8 +107,8 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
107
107
|
final int minor = jdbcDriver.getMinorVersion();
|
108
108
|
if ( major < 5 ) {
|
109
109
|
final RubyClass errorClass = getConnectionNotEstablished(context.runtime);
|
110
|
-
throw context.runtime
|
111
|
-
"MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "");
|
110
|
+
throw new RaiseException(context.runtime, errorClass,
|
111
|
+
"MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "", false);
|
112
112
|
}
|
113
113
|
if ( major == 5 && minor < 1 ) { // need 5.1 for JDBC 4.0
|
114
114
|
// lightweight validation query: "/* ping */ SELECT 1"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
/*
|
2
|
+
* A class to loosen restrictions on the PgResultSetMetaData class,
|
3
|
+
* we need to be able to get the field and the method is currently set to "package".
|
4
|
+
*/
|
5
|
+
package org.postgresql.jdbc;
|
6
|
+
|
7
|
+
import java.sql.SQLException;
|
8
|
+
import org.postgresql.core.BaseConnection;
|
9
|
+
import org.postgresql.core.Field;
|
10
|
+
import org.postgresql.jdbc.PgResultSetMetaData;
|
11
|
+
|
12
|
+
public class PgResultSetMetaDataWrapper {
|
13
|
+
|
14
|
+
private final PgResultSetMetaData metaData;
|
15
|
+
|
16
|
+
public PgResultSetMetaDataWrapper(PgResultSetMetaData metaData) {
|
17
|
+
this.metaData = metaData;
|
18
|
+
}
|
19
|
+
|
20
|
+
public Field getField(int i) throws SQLException {
|
21
|
+
return this.metaData.getField(i);
|
22
|
+
}
|
23
|
+
}
|
@@ -4,7 +4,6 @@ import arjdbc.jdbc.JdbcResult;
|
|
4
4
|
import arjdbc.jdbc.RubyJdbcConnection;
|
5
5
|
|
6
6
|
import java.sql.ResultSet;
|
7
|
-
import java.sql.ResultSetMetaData;
|
8
7
|
import java.sql.SQLException;
|
9
8
|
import java.sql.Types;
|
10
9
|
|
@@ -12,6 +11,7 @@ import org.jruby.Ruby;
|
|
12
11
|
import org.jruby.RubyArray;
|
13
12
|
import org.jruby.RubyClass;
|
14
13
|
import org.jruby.RubyHash;
|
14
|
+
import org.jruby.RubyMethod;
|
15
15
|
import org.jruby.RubyModule;
|
16
16
|
import org.jruby.RubyString;
|
17
17
|
import org.jruby.anno.JRubyMethod;
|
@@ -21,13 +21,17 @@ import org.jruby.runtime.ObjectAllocator;
|
|
21
21
|
import org.jruby.runtime.ThreadContext;
|
22
22
|
import org.jruby.runtime.builtin.IRubyObject;
|
23
23
|
|
24
|
+
import org.postgresql.core.Field;
|
25
|
+
import org.postgresql.jdbc.PgResultSetMetaData;
|
26
|
+
import org.postgresql.jdbc.PgResultSetMetaDataWrapper; // This is a hack unfortunately to get around method scoping
|
27
|
+
|
24
28
|
/*
|
25
29
|
* This class mimics the PG:Result class enough to get by
|
26
30
|
*/
|
27
31
|
public class PostgreSQLResult extends JdbcResult {
|
28
32
|
|
29
33
|
// These are needed when generating an AR::Result
|
30
|
-
private final
|
34
|
+
private final PgResultSetMetaData resultSetMetaData;
|
31
35
|
|
32
36
|
/********* JRuby compat methods ***********/
|
33
37
|
|
@@ -57,7 +61,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
57
61
|
ResultSet resultSet) throws SQLException {
|
58
62
|
super(context, clazz, connection, resultSet);
|
59
63
|
|
60
|
-
resultSetMetaData = resultSet.getMetaData();
|
64
|
+
resultSetMetaData = (PgResultSetMetaData) resultSet.getMetaData();
|
61
65
|
}
|
62
66
|
|
63
67
|
/**
|
@@ -70,28 +74,16 @@ public class PostgreSQLResult extends JdbcResult {
|
|
70
74
|
protected IRubyObject columnTypeMap(final ThreadContext context) throws SQLException {
|
71
75
|
Ruby runtime = context.runtime;
|
72
76
|
RubyHash types = RubyHash.newHash(runtime);
|
77
|
+
PgResultSetMetaDataWrapper mdWrapper = new PgResultSetMetaDataWrapper(resultSetMetaData);
|
73
78
|
int columnCount = columnNames.length;
|
74
79
|
|
75
80
|
IRubyObject adapter = connection.adapter(context);
|
76
81
|
for (int i = 0; i < columnCount; i++) {
|
77
|
-
|
78
|
-
String typeName = resultSetMetaData.getColumnTypeName(col);
|
79
|
-
|
80
|
-
int mod = 0;
|
81
|
-
if ("numeric".equals(typeName)) {
|
82
|
-
// this field is only relevant for "numeric" type in AR
|
83
|
-
// AR checks (fmod - 4 & 0xffff).zero?
|
84
|
-
// pgjdbc:
|
85
|
-
// - for typmod == -1, getScale() and getPrecision() return 0
|
86
|
-
// - for typmod != -1, getScale() returns "(typmod - 4) & 0xFFFF;"
|
87
|
-
mod = resultSetMetaData.getScale(col);
|
88
|
-
mod = mod == 0 && resultSetMetaData.getPrecision(col) == 0 ? -1 : mod + 4;
|
89
|
-
}
|
90
|
-
|
82
|
+
final Field field = mdWrapper.getField(i + 1);
|
91
83
|
final RubyString name = columnNames[i];
|
92
84
|
final IRubyObject type = Helpers.invoke(context, adapter, "get_oid_type",
|
93
|
-
runtime.
|
94
|
-
runtime.newFixnum(
|
85
|
+
runtime.newFixnum(field.getOID()),
|
86
|
+
runtime.newFixnum(field.getMod()),
|
95
87
|
name);
|
96
88
|
|
97
89
|
if (!type.isNil()) types.fastASet(name, type);
|
@@ -152,7 +144,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
152
144
|
* @return ActiveRecord::Result object with the data from this result set
|
153
145
|
* @throws SQLException can be caused by postgres generating its type map
|
154
146
|
*/
|
155
|
-
@Override
|
147
|
+
@Override
|
156
148
|
public IRubyObject toARResult(final ThreadContext context) throws SQLException {
|
157
149
|
RubyClass BinaryDataClass = null;
|
158
150
|
int rowCount = 0;
|
@@ -171,7 +163,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
171
163
|
RubyArray row = (RubyArray) values.eltInternal(rowIndex);
|
172
164
|
IRubyObject value = row.eltInternal(columnIndex);
|
173
165
|
if (value != context.nil) {
|
174
|
-
row.eltInternalSet(columnIndex, BinaryDataClass.newInstance(context, value, Block.NULL_BLOCK));
|
166
|
+
row.eltInternalSet(columnIndex, (IRubyObject) BinaryDataClass.newInstance(context, value, Block.NULL_BLOCK));
|
175
167
|
}
|
176
168
|
}
|
177
169
|
}
|