activerecord-jdbc-alt-adapter 52.5.1-java → 60.2.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 +2 -0
- data/.nvimlog +0 -0
- data/.travis.yml +61 -37
- data/Gemfile +10 -3
- data/README.md +44 -28
- data/Rakefile +1 -1
- data/Rakefile.jdbc +8 -1
- data/activerecord-jdbc-adapter.gemspec +5 -8
- data/activerecord-jdbc-alt-adapter.gemspec +5 -8
- data/lib/arel/visitors/sqlserver.rb +33 -23
- data/lib/arjdbc/abstract/connection_management.rb +7 -0
- data/lib/arjdbc/abstract/core.rb +16 -23
- data/lib/arjdbc/abstract/database_statements.rb +24 -0
- data/lib/arjdbc/abstract/statement_cache.rb +2 -5
- data/lib/arjdbc/abstract/transaction_support.rb +5 -3
- data/lib/arjdbc/db2/column.rb +0 -39
- data/lib/arjdbc/derby/adapter.rb +1 -20
- data/lib/arjdbc/firebird/adapter.rb +0 -21
- data/lib/arjdbc/h2/adapter.rb +0 -15
- data/lib/arjdbc/hsqldb/adapter.rb +0 -14
- data/lib/arjdbc/informix/adapter.rb +0 -23
- data/lib/arjdbc/jdbc/adapter.rb +3 -1
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
- data/lib/arjdbc/jdbc/base_ext.rb +3 -1
- data/lib/arjdbc/jdbc/callbacks.rb +2 -0
- data/lib/arjdbc/jdbc/column.rb +2 -0
- data/lib/arjdbc/jdbc/connection.rb +2 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
- data/lib/arjdbc/jdbc/error.rb +2 -0
- data/lib/arjdbc/jdbc/extension.rb +2 -0
- data/lib/arjdbc/jdbc/java.rb +3 -1
- data/lib/arjdbc/jdbc/railtie.rb +3 -1
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
- data/lib/arjdbc/jdbc/type_cast.rb +2 -0
- data/lib/arjdbc/jdbc/type_converter.rb +2 -0
- data/lib/arjdbc/mssql/adapter.rb +105 -36
- data/lib/arjdbc/mssql/column.rb +5 -1
- data/lib/arjdbc/mssql/connection_methods.rb +8 -2
- data/lib/arjdbc/mssql/database_limits.rb +2 -0
- data/lib/arjdbc/mssql/database_statements.rb +43 -5
- data/lib/arjdbc/mssql/errors.rb +2 -0
- data/lib/arjdbc/mssql/explain_support.rb +3 -1
- data/lib/arjdbc/mssql/extensions/attribute_methods.rb +5 -1
- data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
- data/lib/arjdbc/mssql/quoting.rb +38 -0
- data/lib/arjdbc/mssql/schema_creation.rb +24 -2
- data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
- data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
- data/lib/arjdbc/mssql/schema_statements.rb +63 -21
- data/lib/arjdbc/mssql/transaction.rb +2 -0
- data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
- data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
- data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
- data/lib/arjdbc/mssql/types/string_types.rb +2 -0
- data/lib/arjdbc/mssql/types.rb +2 -0
- data/lib/arjdbc/mssql/utils.rb +2 -0
- data/lib/arjdbc/mssql.rb +3 -1
- data/lib/arjdbc/mysql/adapter.rb +47 -18
- data/lib/arjdbc/postgresql/adapter.rb +240 -214
- data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
- data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
- data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
- data/lib/arjdbc/postgresql/column.rb +6 -4
- data/lib/arjdbc/postgresql/name.rb +2 -0
- data/lib/arjdbc/postgresql/oid_types.rb +3 -1
- data/lib/arjdbc/sqlite3/adapter.rb +188 -180
- data/lib/arjdbc/sqlite3/connection_methods.rb +15 -4
- data/lib/arjdbc/tasks/databases.rake +13 -10
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
- data/lib/arjdbc/util/quoted_cache.rb +3 -1
- data/lib/arjdbc/util/serialized_attributes.rb +3 -1
- data/lib/arjdbc/util/table_copier.rb +3 -1
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +4 -4
- data/rakelib/01-tomcat.rake +2 -2
- data/src/java/arjdbc/ArJdbcModule.java +5 -5
- data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +406 -629
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +56 -30
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
- data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
- metadata +7 -16
|
@@ -57,6 +57,7 @@ import org.jruby.RubyString;
|
|
|
57
57
|
import org.jruby.RubySymbol;
|
|
58
58
|
import org.jruby.RubyTime;
|
|
59
59
|
import org.jruby.anno.JRubyMethod;
|
|
60
|
+
import org.jruby.runtime.Block;
|
|
60
61
|
import org.jruby.runtime.ObjectAllocator;
|
|
61
62
|
import org.jruby.runtime.ThreadContext;
|
|
62
63
|
import org.jruby.runtime.builtin.IRubyObject;
|
|
@@ -203,6 +204,57 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
|
203
204
|
});
|
|
204
205
|
}
|
|
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
|
+
|
|
206
258
|
@Override
|
|
207
259
|
protected Integer jdbcTypeFor(final String type) {
|
|
208
260
|
|
|
@@ -598,6 +650,42 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
|
598
650
|
});
|
|
599
651
|
}
|
|
600
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
|
+
|
|
601
689
|
/**
|
|
602
690
|
* Microsoft SQL 2000+ support schemas
|
|
603
691
|
*/
|
|
@@ -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,
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
262
|
+
return stopCleanupThread != null && stopCleanupThread;
|
|
265
263
|
}
|
|
266
264
|
|
|
267
265
|
private static boolean cleanupThreadShutdown;
|
|
@@ -276,19 +274,11 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
|
276
274
|
catch (ClassNotFoundException e) {
|
|
277
275
|
debugMessage(null, "missing MySQL JDBC cleanup thread ", e);
|
|
278
276
|
}
|
|
279
|
-
catch (NoSuchMethodException e) {
|
|
280
|
-
debugMessage(null, e);
|
|
281
|
-
}
|
|
282
|
-
catch (IllegalAccessException e) {
|
|
277
|
+
catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
|
|
283
278
|
debugMessage(null, e);
|
|
284
|
-
}
|
|
285
|
-
catch (InvocationTargetException e) {
|
|
279
|
+
} catch (InvocationTargetException e) {
|
|
286
280
|
debugMessage(null, e.getTargetException());
|
|
287
|
-
}
|
|
288
|
-
catch (SecurityException e) {
|
|
289
|
-
debugMessage(null, e);
|
|
290
|
-
}
|
|
291
|
-
finally { cleanupThreadShutdown = true; }
|
|
281
|
+
} finally { cleanupThreadShutdown = true; }
|
|
292
282
|
}
|
|
293
283
|
|
|
294
284
|
}
|
|
@@ -35,27 +35,19 @@ import arjdbc.util.StringHelper;
|
|
|
35
35
|
import java.io.ByteArrayInputStream;
|
|
36
36
|
import java.lang.StringBuilder;
|
|
37
37
|
import java.lang.reflect.InvocationTargetException;
|
|
38
|
-
import java.
|
|
39
|
-
import java.sql
|
|
38
|
+
import java.math.BigDecimal;
|
|
39
|
+
import java.sql.*;
|
|
40
40
|
import java.sql.Date;
|
|
41
|
-
import java.
|
|
42
|
-
import java.sql.ResultSet;
|
|
43
|
-
import java.sql.ResultSetMetaData;
|
|
44
|
-
import java.sql.SQLException;
|
|
45
|
-
import java.sql.Timestamp;
|
|
46
|
-
import java.sql.Types;
|
|
47
|
-
import java.util.ArrayList;
|
|
48
|
-
import java.util.Collections;
|
|
49
|
-
import java.util.HashMap;
|
|
50
|
-
import java.util.Map;
|
|
51
|
-
import java.util.UUID;
|
|
41
|
+
import java.util.*;
|
|
52
42
|
import java.util.regex.Pattern;
|
|
53
43
|
import java.util.regex.Matcher;
|
|
54
44
|
|
|
45
|
+
import org.joda.time.DateTime;
|
|
55
46
|
import org.jruby.*;
|
|
56
47
|
import org.jruby.anno.JRubyMethod;
|
|
57
48
|
import org.jruby.exceptions.RaiseException;
|
|
58
49
|
import org.jruby.ext.bigdecimal.RubyBigDecimal;
|
|
50
|
+
import org.jruby.ext.date.RubyDate;
|
|
59
51
|
import org.jruby.javasupport.JavaUtil;
|
|
60
52
|
import org.jruby.runtime.ObjectAllocator;
|
|
61
53
|
import org.jruby.runtime.ThreadContext;
|
|
@@ -115,6 +107,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
115
107
|
|
|
116
108
|
// Used to wipe trailing 0's from points (3.0, 5.6) -> (3, 5.6)
|
|
117
109
|
private static final Pattern pointCleanerPattern = Pattern.compile("\\.0\\b");
|
|
110
|
+
private static final TimeZone TZ_DEFAULT = TimeZone.getDefault();
|
|
118
111
|
|
|
119
112
|
private RubyClass resultClass;
|
|
120
113
|
private RubyHash typeMap = null;
|
|
@@ -163,15 +156,12 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
163
156
|
try { // public static String getVersion()
|
|
164
157
|
final String version = (String) // "PostgreSQL 9.2 JDBC4 (build 1002)"
|
|
165
158
|
jdbcDriver.getClass().getMethod("getVersion").invoke(null);
|
|
166
|
-
if ( version != null && version.
|
|
159
|
+
if ( version != null && version.contains("JDBC3")) {
|
|
167
160
|
// config[:connection_alive_sql] ||= 'SELECT 1'
|
|
168
161
|
setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("SELECT 1"));
|
|
169
162
|
}
|
|
170
163
|
}
|
|
171
|
-
catch (NoSuchMethodException
|
|
172
|
-
catch (SecurityException e) { }
|
|
173
|
-
catch (IllegalAccessException e) { }
|
|
174
|
-
catch (InvocationTargetException e) { }
|
|
164
|
+
catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException ignored) { }
|
|
175
165
|
}
|
|
176
166
|
|
|
177
167
|
return driverWrapper;
|
|
@@ -218,11 +208,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
218
208
|
|
|
219
209
|
@JRubyMethod(name = "database_product")
|
|
220
210
|
public IRubyObject database_product(final ThreadContext context) {
|
|
221
|
-
return withConnection(context,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
|
|
225
|
-
}
|
|
211
|
+
return withConnection(context, (Callable<IRubyObject>) connection -> {
|
|
212
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
|
213
|
+
return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
|
|
226
214
|
});
|
|
227
215
|
}
|
|
228
216
|
|
|
@@ -308,6 +296,24 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
308
296
|
statement.setArray(index, connection.createArrayOf(typeName, values));
|
|
309
297
|
}
|
|
310
298
|
|
|
299
|
+
protected void setDecimalParameter(final ThreadContext context,
|
|
300
|
+
final Connection connection, final PreparedStatement statement,
|
|
301
|
+
final int index, final IRubyObject value,
|
|
302
|
+
final IRubyObject attribute, final int type) throws SQLException {
|
|
303
|
+
if (value instanceof RubyBigDecimal) {
|
|
304
|
+
RubyBigDecimal bigDecimal = (RubyBigDecimal) value;
|
|
305
|
+
|
|
306
|
+
// too bad RubyBigDecimal.isNaN() isn't public
|
|
307
|
+
if (bigDecimal.nan_p(context) == context.tru) {
|
|
308
|
+
statement.setDouble(index, Double.NaN);
|
|
309
|
+
} else {
|
|
310
|
+
statement.setBigDecimal(index, bigDecimal.getValue());
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
super.setDecimalParameter(context, connection, statement, index, value, attribute, type);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
311
317
|
@Override
|
|
312
318
|
protected void setBlobParameter(final ThreadContext context,
|
|
313
319
|
final Connection connection, final PreparedStatement statement,
|
|
@@ -377,14 +383,29 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
377
383
|
value = value.callMethod(context, "to_date");
|
|
378
384
|
}
|
|
379
385
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
386
|
+
if (value instanceof RubyDate) {
|
|
387
|
+
RubyDate rubyDate = (RubyDate) value;
|
|
388
|
+
DateTime dt = rubyDate.getDateTime();
|
|
389
|
+
// pgjdbc needs adjustment for default JVM timezone
|
|
390
|
+
//
|
|
391
|
+
// If the date is a day when Daylight savings starts (2am changed to 3am),
|
|
392
|
+
// And we are in a positive GMT timezone (Australia/Melbourne)
|
|
393
|
+
// Then removing milliseconds equal to the TimeZone (+11 GMT),
|
|
394
|
+
// will result in the date being the previous day 23:00, because of the missing hour.
|
|
395
|
+
// So we check if the date after the shift is outside of daylight time and remove an hours worth of milliseconds.
|
|
396
|
+
final long dateMillis = dt.getMillis();
|
|
397
|
+
long offset = TZ_DEFAULT.getOffset(dt.getMillis());
|
|
398
|
+
if (TZ_DEFAULT.inDaylightTime(new Date(dt.getMillis())) && !TZ_DEFAULT.inDaylightTime(new Date(dateMillis - offset))) {
|
|
399
|
+
offset -= 3600000; // 1 hour
|
|
400
|
+
}
|
|
401
|
+
Date utcShiftedDate = new Date(dateMillis - offset);
|
|
383
402
|
|
|
384
|
-
|
|
385
|
-
|
|
403
|
+
statement.setDate(index, utcShiftedDate);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
386
406
|
|
|
387
|
-
|
|
407
|
+
// NOTE: assuming Date#to_s does right ...
|
|
408
|
+
statement.setDate(index, Date.valueOf(value.toString()));
|
|
388
409
|
}
|
|
389
410
|
|
|
390
411
|
@Override
|
|
@@ -514,7 +535,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
514
535
|
|
|
515
536
|
private static Double[] parseDoubles(IRubyObject value) {
|
|
516
537
|
Matcher matches = doubleValuePattern.matcher(value.toString());
|
|
517
|
-
ArrayList<Double> doubles = new ArrayList
|
|
538
|
+
ArrayList<Double> doubles = new ArrayList<>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
|
|
518
539
|
|
|
519
540
|
while ( matches.find() ) {
|
|
520
541
|
doubles.add(Double.parseDouble(matches.group()));
|
|
@@ -729,6 +750,11 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
|
729
750
|
return DateTimeUtils.parseDate(context, value, getDefaultTimeZone(context));
|
|
730
751
|
}
|
|
731
752
|
|
|
753
|
+
protected IRubyObject decimalToRuby(final ThreadContext context,
|
|
754
|
+
final Ruby runtime, final ResultSet resultSet, final int column) throws SQLException {
|
|
755
|
+
if ("NaN".equals(resultSet.getString(column))) return new RubyBigDecimal(runtime, BigDecimal.ZERO, true);
|
|
756
|
+
return super.decimalToRuby(context, runtime, resultSet, column);
|
|
757
|
+
}
|
|
732
758
|
|
|
733
759
|
/**
|
|
734
760
|
* Detects PG specific types and converts them to their Ruby equivalents
|
|
@@ -100,55 +100,53 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
100
100
|
public IRubyObject encoding(final ThreadContext context) throws SQLException {
|
|
101
101
|
if (encoding != null) return encoding;
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
} catch (final SQLException e) {
|
|
120
|
-
debugErrorSQL(context, query);
|
|
121
|
-
throw e;
|
|
122
|
-
} finally {
|
|
123
|
-
close(resultSet);
|
|
124
|
-
close(statement);
|
|
103
|
+
// FIXME: How many single result queries do we have in Java?
|
|
104
|
+
return withConnection(context, connection -> {
|
|
105
|
+
String query = "PRAGMA encoding";
|
|
106
|
+
Statement statement = null;
|
|
107
|
+
ResultSet resultSet = null;
|
|
108
|
+
try {
|
|
109
|
+
statement = createStatement(context, connection);
|
|
110
|
+
if (statement.execute(query)) {
|
|
111
|
+
// Enebo: I do not think we need to worry about failure here?
|
|
112
|
+
resultSet = statement.getResultSet();
|
|
113
|
+
if (!resultSet.next()) return context.nil;
|
|
114
|
+
String encodingString = resultSet.getString(1);
|
|
115
|
+
|
|
116
|
+
encoding = cachedString(context, encodingString);
|
|
117
|
+
|
|
118
|
+
return encoding;
|
|
125
119
|
}
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
} catch (final SQLException e) {
|
|
121
|
+
debugErrorSQL(context, query);
|
|
122
|
+
throw e;
|
|
123
|
+
} finally {
|
|
124
|
+
close(resultSet);
|
|
125
|
+
close(statement);
|
|
128
126
|
}
|
|
127
|
+
|
|
128
|
+
return context.nil;
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
@JRubyMethod(name = {"last_insert_rowid", "last_insert_id"}, alias = "last_insert_row_id")
|
|
133
133
|
public IRubyObject last_insert_rowid(final ThreadContext context)
|
|
134
134
|
throws SQLException {
|
|
135
|
-
return withConnection(context,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
throw e;
|
|
149
|
-
}
|
|
150
|
-
finally { close(genKeys); close(statement); }
|
|
135
|
+
return withConnection(context, connection -> {
|
|
136
|
+
Statement statement = null; ResultSet genKeys = null;
|
|
137
|
+
try {
|
|
138
|
+
statement = connection.createStatement();
|
|
139
|
+
// NOTE: strangely this will work and has been used for quite some time :
|
|
140
|
+
//return mapGeneratedKeys(context.getRuntime(), connection, statement, true);
|
|
141
|
+
// but we should assume SQLite JDBC will prefer sane API usage eventually :
|
|
142
|
+
genKeys = statement.executeQuery("SELECT last_insert_rowid()");
|
|
143
|
+
return doMapGeneratedKeys(context.runtime, genKeys, true);
|
|
144
|
+
}
|
|
145
|
+
catch (final SQLException e) {
|
|
146
|
+
debugMessage(context.runtime, "failed to get generated keys: ", e);
|
|
147
|
+
throw e;
|
|
151
148
|
}
|
|
149
|
+
finally { close(genKeys); close(statement); }
|
|
152
150
|
});
|
|
153
151
|
}
|
|
154
152
|
|
|
@@ -183,66 +181,64 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
183
181
|
final String tableName = table;
|
|
184
182
|
final String schemaName = schema;
|
|
185
183
|
// return super.indexes(context, tableName, name, schemaName);
|
|
186
|
-
return withConnection(context,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
final RubyClass IndexDefinition = getIndexDefinition(runtime);
|
|
184
|
+
return withConnection(context, (Callable<IRubyObject>) connection -> {
|
|
185
|
+
final Ruby runtime = context.runtime;
|
|
186
|
+
final RubyClass IndexDefinition = getIndexDefinition(runtime);
|
|
190
187
|
|
|
191
|
-
|
|
188
|
+
final TableName table1 = extractTableName(connection, null, schemaName, tableName);
|
|
192
189
|
|
|
193
|
-
|
|
190
|
+
final List<RubyString> primaryKeys = primaryKeys(context, connection, table1);
|
|
194
191
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
throw e;
|
|
192
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
|
193
|
+
ResultSet indexInfoSet;
|
|
194
|
+
try {
|
|
195
|
+
indexInfoSet = metaData.getIndexInfo(table1.catalog, table1.schema, table1.name, false, true);
|
|
196
|
+
}
|
|
197
|
+
catch (SQLException e) {
|
|
198
|
+
final String msg = e.getMessage();
|
|
199
|
+
if ( msg != null && msg.startsWith("[SQLITE_ERROR] SQL error or missing database") ) {
|
|
200
|
+
return RubyArray.newEmptyArray(runtime); // on 3.8.7 getIndexInfo fails if table has no indexes
|
|
206
201
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
|
|
213
|
-
if ( indexName == null ) continue;
|
|
214
|
-
RubyArray currentColumns = null;
|
|
202
|
+
throw e;
|
|
203
|
+
}
|
|
204
|
+
final RubyArray indexes = RubyArray.newArray(runtime, 8);
|
|
205
|
+
try {
|
|
206
|
+
String currentIndex = null;
|
|
215
207
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
208
|
+
while ( indexInfoSet.next() ) {
|
|
209
|
+
String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
|
|
210
|
+
if ( indexName == null ) continue;
|
|
211
|
+
RubyArray currentColumns = null;
|
|
219
212
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
213
|
+
final String columnName = indexInfoSet.getString(INDEX_INFO_COLUMN_NAME);
|
|
214
|
+
final RubyString rubyColumnName = cachedString(context, columnName);
|
|
215
|
+
if ( primaryKeys.contains(rubyColumnName) ) continue;
|
|
223
216
|
|
|
224
|
-
|
|
217
|
+
// We are working on a new index
|
|
218
|
+
if ( ! indexName.equals(currentIndex) ) {
|
|
219
|
+
currentIndex = indexName;
|
|
225
220
|
|
|
226
|
-
|
|
221
|
+
String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
|
|
227
222
|
|
|
228
|
-
|
|
229
|
-
cachedString(context, indexTableName), // table_name
|
|
230
|
-
cachedString(context, indexName), // index_name
|
|
231
|
-
nonUnique ? runtime.getFalse() : runtime.getTrue(), // unique
|
|
232
|
-
currentColumns = RubyArray.newArray(runtime, 4) // [] column names
|
|
233
|
-
};
|
|
223
|
+
final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
|
|
234
224
|
|
|
235
|
-
|
|
236
|
-
|
|
225
|
+
IRubyObject[] args = new IRubyObject[] {
|
|
226
|
+
cachedString(context, indexTableName), // table_name
|
|
227
|
+
cachedString(context, indexName), // index_name
|
|
228
|
+
nonUnique ? context.fals : context.tru, // unique
|
|
229
|
+
currentColumns = RubyArray.newArray(runtime, 4) // [] column names
|
|
230
|
+
};
|
|
237
231
|
|
|
238
|
-
|
|
239
|
-
if ( currentColumns != null ) currentColumns.append(rubyColumnName);
|
|
232
|
+
indexes.append( IndexDefinition.newInstance(context, args, Block.NULL_BLOCK) ); // IndexDefinition.new
|
|
240
233
|
}
|
|
241
234
|
|
|
242
|
-
|
|
235
|
+
// one or more columns can be associated with an index
|
|
236
|
+
if ( currentColumns != null ) currentColumns.append(rubyColumnName);
|
|
237
|
+
}
|
|
243
238
|
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
return indexes;
|
|
240
|
+
|
|
241
|
+
} finally { close(indexInfoSet); }
|
|
246
242
|
});
|
|
247
243
|
}
|
|
248
244
|
|
|
@@ -390,8 +386,9 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
390
386
|
"create_savepoint (without name) not implemented!"
|
|
391
387
|
);
|
|
392
388
|
}
|
|
393
|
-
|
|
389
|
+
Statement statement = null;
|
|
394
390
|
try {
|
|
391
|
+
final Connection connection = getConnectionInternal(true);
|
|
395
392
|
connection.setAutoCommit(false);
|
|
396
393
|
// NOTE: JDBC driver does not support setSavepoint(String) :
|
|
397
394
|
( statement = connection.createStatement() ).execute("SAVEPOINT " + name.toString());
|
|
@@ -411,12 +408,13 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
411
408
|
public IRubyObject rollback_savepoint(final ThreadContext context, final IRubyObject name) {
|
|
412
409
|
if ( useSavepointAPI(context) ) return super.rollback_savepoint(context, name);
|
|
413
410
|
|
|
414
|
-
|
|
411
|
+
Statement statement = null;
|
|
415
412
|
try {
|
|
416
413
|
if ( getSavepoints(context).get(name) == null ) {
|
|
417
414
|
throw newSavepointNotSetError(context, name, "rollback");
|
|
418
415
|
}
|
|
419
416
|
// NOTE: JDBC driver does not implement rollback(Savepoint) :
|
|
417
|
+
final Connection connection = getConnectionInternal(true);
|
|
420
418
|
( statement = connection.createStatement() ).execute("ROLLBACK TO SAVEPOINT " + name.toString());
|
|
421
419
|
|
|
422
420
|
return context.nil;
|
|
@@ -434,12 +432,13 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
434
432
|
public IRubyObject release_savepoint(final ThreadContext context, final IRubyObject name) {
|
|
435
433
|
if ( useSavepointAPI(context) ) return super.release_savepoint(context, name);
|
|
436
434
|
|
|
437
|
-
|
|
435
|
+
Statement statement = null;
|
|
438
436
|
try {
|
|
439
437
|
if ( getSavepoints(context).remove(name) == null ) {
|
|
440
438
|
throw newSavepointNotSetError(context, name, "release");
|
|
441
439
|
}
|
|
442
440
|
// NOTE: JDBC driver does not implement release(Savepoint) :
|
|
441
|
+
final Connection connection = getConnectionInternal(true);
|
|
443
442
|
( statement = connection.createStatement() ).execute("RELEASE SAVEPOINT " + name.toString());
|
|
444
443
|
return context.nil;
|
|
445
444
|
} catch (SQLException e) {
|
|
@@ -453,19 +452,15 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
453
452
|
// a consistent JDBC layer.
|
|
454
453
|
@JRubyMethod(name = "supports_savepoints?")
|
|
455
454
|
public IRubyObject supports_savepoints_p(final ThreadContext context) throws SQLException {
|
|
456
|
-
return context.
|
|
455
|
+
return context.tru;
|
|
457
456
|
}
|
|
458
457
|
|
|
459
|
-
@
|
|
460
|
-
|
|
461
|
-
final Connection connection
|
|
462
|
-
|
|
463
|
-
final IRubyObject attribute, final int type) throws SQLException {
|
|
464
|
-
// Apparently active record stores booleans in sqlite as 't' and 'f' instead of the built in 1/0
|
|
465
|
-
statement.setString(index, value.isTrue() ? "t" : "f");
|
|
458
|
+
@JRubyMethod(name = "readonly?")
|
|
459
|
+
public IRubyObject readonly_p(final ThreadContext context) throws SQLException {
|
|
460
|
+
final Connection connection = getConnection(true);
|
|
461
|
+
return context.runtime.newBoolean(connection.isReadOnly());
|
|
466
462
|
}
|
|
467
463
|
|
|
468
|
-
|
|
469
464
|
@Override
|
|
470
465
|
protected void setDecimalParameter(final ThreadContext context,
|
|
471
466
|
final Connection connection, final PreparedStatement statement,
|
|
@@ -518,7 +513,7 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
|
518
513
|
final int index, IRubyObject value,
|
|
519
514
|
final IRubyObject attribute, final int type) throws SQLException {
|
|
520
515
|
|
|
521
|
-
if (value instanceof RubyTime) value = ((RubyTime) value).strftime(TIMESTAMP_FORMAT);
|
|
516
|
+
if (value instanceof RubyTime) value = ((RubyTime) value).strftime(context, TIMESTAMP_FORMAT);
|
|
522
517
|
|
|
523
518
|
setStringParameter(context, connection, statement, index, value, attribute, type);
|
|
524
519
|
}
|