activerecord-jdbc-adapter 50.2-java → 50.3-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 +5 -5
- data/.travis.yml +36 -15
- data/lib/arjdbc/abstract/database_statements.rb +2 -2
- data/lib/arjdbc/abstract/statement_cache.rb +4 -4
- data/lib/arjdbc/mysql/adapter.rb +8 -0
- data/lib/arjdbc/postgresql/adapter.rb +1 -58
- data/lib/arjdbc/postgresql/oid_types.rb +81 -7
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/rails.rake +4 -3
- data/src/java/arjdbc/ArJdbcModule.java +5 -15
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +0 -87
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +0 -1
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +47 -19
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +21 -13
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +30 -44
- data/src/java/arjdbc/util/DateTimeUtils.java +119 -0
- data/src/java/arjdbc/util/QuotingUtils.java +6 -7
- metadata +5 -5
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -24
- data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +0 -23
@@ -68,7 +68,6 @@ final class DataSourceConnectionFactory implements ConnectionFactory {
|
|
68
68
|
|
69
69
|
@Override
|
70
70
|
public Connection newConnection() throws SQLException {
|
71
|
-
DataSource dataSource = this.dataSource;
|
72
71
|
// in case DS failed previously look it up again from JNDI :
|
73
72
|
if (dataSource == null) {
|
74
73
|
lookupDataSource();
|
@@ -0,0 +1,61 @@
|
|
1
|
+
/***** BEGIN LICENSE BLOCK *****
|
2
|
+
* Copyright (c) 2012-2014 Karol Bucek <self@kares.org>
|
3
|
+
* Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
|
4
|
+
* Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
* a copy of this software and associated documentation files (the
|
8
|
+
* "Software"), to deal in the Software without restriction, including
|
9
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
* the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be
|
15
|
+
* included in all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
***** END LICENSE BLOCK *****/
|
25
|
+
|
26
|
+
package arjdbc.jdbc;
|
27
|
+
|
28
|
+
import java.sql.Connection;
|
29
|
+
import java.sql.SQLException;
|
30
|
+
|
31
|
+
import org.jruby.RubyObject;
|
32
|
+
import org.jruby.RubyString;
|
33
|
+
import org.jruby.runtime.ThreadContext;
|
34
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
35
|
+
|
36
|
+
// @legacy ActiveRecord::ConnectionAdapters::JdbcDriver
|
37
|
+
class RubyConnectionFactory implements ConnectionFactory {
|
38
|
+
|
39
|
+
private final IRubyObject driver;
|
40
|
+
final RubyString url;
|
41
|
+
final IRubyObject username, password; // null allowed
|
42
|
+
|
43
|
+
private final RubyObject contextProvider;
|
44
|
+
|
45
|
+
public RubyConnectionFactory(final IRubyObject driver, final RubyString url,
|
46
|
+
final IRubyObject username, final IRubyObject password) {
|
47
|
+
this.driver = driver; this.url = url;
|
48
|
+
this.username = username; this.password = password;
|
49
|
+
contextProvider = (RubyObject) driver;
|
50
|
+
}
|
51
|
+
|
52
|
+
@Override
|
53
|
+
public Connection newConnection() throws SQLException {
|
54
|
+
final ThreadContext context = contextProvider.getRuntime().getCurrentContext();
|
55
|
+
final IRubyObject connection = driver.callMethod(context, "connection", new IRubyObject[] { url, username, password });
|
56
|
+
return (Connection) connection.toJava(Connection.class);
|
57
|
+
}
|
58
|
+
|
59
|
+
IRubyObject getDriver() { return driver; } /* for tests */
|
60
|
+
|
61
|
+
}
|
@@ -128,6 +128,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
128
128
|
private boolean lazy = false; // final once set on initialize
|
129
129
|
private boolean jndi; // final once set on initialize
|
130
130
|
private boolean configureConnection = true; // final once initialized
|
131
|
+
private int fetchSize = 0; // 0 = JDBC default
|
131
132
|
|
132
133
|
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
133
134
|
super(runtime, metaClass);
|
@@ -571,6 +572,11 @@ public class RubyJdbcConnection extends RubyObject {
|
|
571
572
|
else {
|
572
573
|
this.configureConnection = value != context.runtime.getFalse();
|
573
574
|
}
|
575
|
+
|
576
|
+
IRubyObject jdbcFetchSize = getConfigValue(context, "jdbc_fetch_size");
|
577
|
+
if (jdbcFetchSize != context.nil) {
|
578
|
+
this.fetchSize = RubyNumeric.fix2int(jdbcFetchSize);
|
579
|
+
}
|
574
580
|
}
|
575
581
|
|
576
582
|
@JRubyMethod(name = "adapter")
|
@@ -818,6 +824,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
818
824
|
// is called, so we have to process the result sets as we get them
|
819
825
|
// this shouldn't be an issue in most cases since we're only getting 1 result set anyways
|
820
826
|
result = mapExecuteResult(context, connection, resultSet);
|
827
|
+
resultSet.close();
|
821
828
|
} else {
|
822
829
|
result = context.runtime.newFixnum(updateCount);
|
823
830
|
}
|
@@ -851,6 +858,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
851
858
|
else {
|
852
859
|
statement.setEscapeProcessing(escapeProcessing.isTrue());
|
853
860
|
}
|
861
|
+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
854
862
|
return statement;
|
855
863
|
}
|
856
864
|
|
@@ -1045,6 +1053,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1045
1053
|
else {
|
1046
1054
|
final PreparedStatement prepStatement;
|
1047
1055
|
statement = prepStatement = connection.prepareStatement(query);
|
1056
|
+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1048
1057
|
statement.setMaxRows(maxRows); // zero means there is no limit
|
1049
1058
|
setStatementParameters(context, connection, prepStatement, binds);
|
1050
1059
|
hasResult = prepStatement.execute();
|
@@ -1112,6 +1121,24 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1112
1121
|
});
|
1113
1122
|
}
|
1114
1123
|
|
1124
|
+
/**
|
1125
|
+
* Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
|
1126
|
+
* @param context which context this method is executing on.
|
1127
|
+
* @param sql the query to prepare-
|
1128
|
+
* @return a Ruby <code>PreparedStatement</code>
|
1129
|
+
*/
|
1130
|
+
@JRubyMethod(required = 1)
|
1131
|
+
public IRubyObject prepare_statement(final ThreadContext context, final IRubyObject sql) {
|
1132
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
1133
|
+
public IRubyObject call(Connection connection) throws SQLException {
|
1134
|
+
final String query = sql.convertToString().getUnicodeValue();
|
1135
|
+
PreparedStatement statement = connection.prepareStatement(query);
|
1136
|
+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1137
|
+
return JavaUtil.convertJavaToRuby(context.runtime, statement);
|
1138
|
+
}
|
1139
|
+
});
|
1140
|
+
}
|
1141
|
+
|
1115
1142
|
// Called from exec_query in abstract/database_statements
|
1116
1143
|
/**
|
1117
1144
|
* Executes a query and returns the (AR) result. There are three parameters:
|
@@ -1142,6 +1169,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1142
1169
|
statement = (PreparedStatement) JavaEmbedUtils.rubyToJava(cachedStatement);
|
1143
1170
|
} else {
|
1144
1171
|
statement = connection.prepareStatement(query);
|
1172
|
+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1145
1173
|
}
|
1146
1174
|
|
1147
1175
|
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
@@ -1149,12 +1177,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1149
1177
|
if (statement.execute()) {
|
1150
1178
|
ResultSet resultSet = statement.getResultSet();
|
1151
1179
|
IRubyObject results = mapQueryResult(context, connection, resultSet);
|
1152
|
-
|
1153
|
-
if (cached) {
|
1154
|
-
// Make sure we free the result set if we are caching the statement
|
1155
|
-
// It gets closed automatically when the statement is closed if we aren't caching
|
1156
|
-
resultSet.close();
|
1157
|
-
}
|
1180
|
+
resultSet.close();
|
1158
1181
|
|
1159
1182
|
return results;
|
1160
1183
|
} else {
|
@@ -1807,7 +1830,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1807
1830
|
if ( url.isNil() || ( driver.isNil() && driver_instance.isNil() ) ) {
|
1808
1831
|
final Ruby runtime = context.runtime;
|
1809
1832
|
final RubyClass errorClass = getConnectionNotEstablished( runtime );
|
1810
|
-
throw
|
1833
|
+
throw runtime.newRaiseException(errorClass, "adapter requires :driver class and jdbc :url");
|
1811
1834
|
}
|
1812
1835
|
|
1813
1836
|
final String jdbcURL = buildURL(context, url);
|
@@ -1824,7 +1847,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1824
1847
|
return factory;
|
1825
1848
|
}
|
1826
1849
|
else {
|
1827
|
-
setConnectionFactory(factory = new
|
1850
|
+
setConnectionFactory(factory = new RubyConnectionFactory(
|
1828
1851
|
driver_instance, context.runtime.newString(jdbcURL),
|
1829
1852
|
( username.isNil() ? username : username.asString() ),
|
1830
1853
|
( password.isNil() ? password : password.asString() )
|
@@ -2485,6 +2508,8 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2485
2508
|
while ( arrayResult.next() ) {
|
2486
2509
|
array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
|
2487
2510
|
}
|
2511
|
+
arrayResult.close();
|
2512
|
+
|
2488
2513
|
return array;
|
2489
2514
|
}
|
2490
2515
|
finally { if ( value != null ) value.free(); }
|
@@ -2683,7 +2708,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2683
2708
|
}
|
2684
2709
|
|
2685
2710
|
protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
|
2686
|
-
return timeInDefaultTimeZone(context, toTime(context, value));
|
2711
|
+
return timeInDefaultTimeZone(context, DateTimeUtils.toTime(context, value));
|
2687
2712
|
}
|
2688
2713
|
|
2689
2714
|
protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final RubyTime time) {
|
@@ -2695,11 +2720,14 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2695
2720
|
return timeInDefaultTZ;
|
2696
2721
|
}
|
2697
2722
|
|
2723
|
+
protected final DateTime dateTimeInDefaultTimeZone(final ThreadContext context, final DateTime dateTime) {
|
2724
|
+
final DateTimeZone defaultZone = getDefaultTimeZone(context);
|
2725
|
+
if (defaultZone == dateTime.getZone()) return dateTime;
|
2726
|
+
return dateTime.withZone(defaultZone);
|
2727
|
+
}
|
2728
|
+
|
2698
2729
|
public static RubyTime toTime(final ThreadContext context, final IRubyObject value) {
|
2699
|
-
|
2700
|
-
return (RubyTime) TypeConverter.convertToTypeWithCheck(value, context.runtime.getTime(), "to_time");
|
2701
|
-
}
|
2702
|
-
return (RubyTime) value;
|
2730
|
+
return DateTimeUtils.toTime(context, value);
|
2703
2731
|
}
|
2704
2732
|
|
2705
2733
|
protected boolean isDefaultTimeZoneUTC(final ThreadContext context) {
|
@@ -2799,8 +2827,8 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2799
2827
|
final int index, IRubyObject value,
|
2800
2828
|
final IRubyObject attribute, final int type) throws SQLException {
|
2801
2829
|
|
2802
|
-
final RubyTime timeValue =
|
2803
|
-
final DateTime dateTime = timeValue.getDateTime();
|
2830
|
+
final RubyTime timeValue = DateTimeUtils.toTime(context, value);
|
2831
|
+
final DateTime dateTime = dateTimeInDefaultTimeZone(context, timeValue.getDateTime());
|
2804
2832
|
final Timestamp timestamp = new Timestamp(dateTime.getMillis());
|
2805
2833
|
// 1942-11-30T01:02:03.123_456
|
2806
2834
|
if (timeValue.getNSec() > 0) timestamp.setNanos((int) (timestamp.getNanos() + timeValue.getNSec()));
|
@@ -2894,7 +2922,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2894
2922
|
final RubySymbol type = (RubySymbol) attributeSQLType(context, attribute);
|
2895
2923
|
|
2896
2924
|
// For some reason the driver doesn't like "character varying" as a type
|
2897
|
-
if ( type.eql(context.runtime.newSymbol("string")) ) return "
|
2925
|
+
if ( type.eql(context.runtime.newSymbol("string")) ) return "varchar";
|
2898
2926
|
|
2899
2927
|
final RubyHash nativeTypes = (RubyHash) getAdapter().callMethod(context, "native_database_types");
|
2900
2928
|
// e.g. `integer: { name: 'integer' }`
|
@@ -3001,7 +3029,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3001
3029
|
private void handleNotConnected() {
|
3002
3030
|
final Ruby runtime = getRuntime();
|
3003
3031
|
final RubyClass errorClass = getConnectionNotEstablished( runtime );
|
3004
|
-
throw
|
3032
|
+
throw runtime.newRaiseException(errorClass, "no connection available");
|
3005
3033
|
}
|
3006
3034
|
|
3007
3035
|
/**
|
@@ -3538,7 +3566,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3538
3566
|
return (RaiseException) exception;
|
3539
3567
|
}
|
3540
3568
|
if ( exception instanceof RuntimeException ) {
|
3541
|
-
return
|
3569
|
+
return wrapException(context, context.runtime.getRuntimeError(), exception);
|
3542
3570
|
}
|
3543
3571
|
// NOTE: compat - maybe makes sense or maybe not (e.g. IOException) :
|
3544
3572
|
return wrapException(context, getJDBCError(runtime), exception);
|
@@ -3551,7 +3579,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3551
3579
|
|
3552
3580
|
public static RaiseException wrapException(final ThreadContext context,
|
3553
3581
|
final RubyClass errorClass, final Throwable exception, final String message) {
|
3554
|
-
final RaiseException error =
|
3582
|
+
final RaiseException error = context.runtime.newRaiseException(errorClass, message);
|
3555
3583
|
error.initCause(exception);
|
3556
3584
|
return error;
|
3557
3585
|
}
|
@@ -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
|
111
|
-
"MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + ""
|
110
|
+
throw context.runtime.newRaiseException(errorClass,
|
111
|
+
"MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "");
|
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,52 @@
|
|
1
|
+
package arjdbc.postgresql;
|
2
|
+
|
3
|
+
import arjdbc.util.DateTimeUtils;
|
4
|
+
import org.joda.time.DateTimeZone;
|
5
|
+
import org.jruby.RubyArray;
|
6
|
+
import org.jruby.RubyFloat;
|
7
|
+
import org.jruby.runtime.ThreadContext;
|
8
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
9
|
+
|
10
|
+
/**
|
11
|
+
* PostgreSQL specific DateTime/Timestamp helpers
|
12
|
+
* @author dritz
|
13
|
+
*/
|
14
|
+
public abstract class PgDateTimeUtils extends DateTimeUtils {
|
15
|
+
/**
|
16
|
+
* Convert a ruby value to a PostgreSQL timestamp string
|
17
|
+
* @param context
|
18
|
+
* @param value Ruby value, typically a Time instance
|
19
|
+
* @param zone DateTimeZone to adjust to, optional
|
20
|
+
* @param withZone include timezone in string?
|
21
|
+
* @return A string fit for PostgreSQL
|
22
|
+
*/
|
23
|
+
public static String timestampValueToString(final ThreadContext context, IRubyObject value, DateTimeZone zone,
|
24
|
+
boolean withZone) {
|
25
|
+
if (value instanceof RubyFloat) {
|
26
|
+
final double dv = ((RubyFloat) value).getValue();
|
27
|
+
if (dv == Double.POSITIVE_INFINITY) {
|
28
|
+
return "infinity";
|
29
|
+
} else if (dv == Double.NEGATIVE_INFINITY) {
|
30
|
+
return "-infinity";
|
31
|
+
}
|
32
|
+
}
|
33
|
+
return timestampTimeToString(context, value, zone, withZone);
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Converts a RubyArray with timestamp values to a java array of PostgreSQL timestamp strings
|
38
|
+
* @param context
|
39
|
+
* @param rubyArray
|
40
|
+
* @return Array of timestamp strings
|
41
|
+
*/
|
42
|
+
public static String[] timestampStringArray(final ThreadContext context, final RubyArray rubyArray) {
|
43
|
+
int size = rubyArray.size();
|
44
|
+
String[] values = new String[size];
|
45
|
+
|
46
|
+
for (int i = 0; i < size; i++) {
|
47
|
+
IRubyObject elem = rubyArray.eltInternal(i);
|
48
|
+
values[i] = timestampValueToString(context, elem, DateTimeZone.UTC, false);
|
49
|
+
}
|
50
|
+
return values;
|
51
|
+
}
|
52
|
+
}
|
@@ -4,6 +4,7 @@ import arjdbc.jdbc.JdbcResult;
|
|
4
4
|
import arjdbc.jdbc.RubyJdbcConnection;
|
5
5
|
|
6
6
|
import java.sql.ResultSet;
|
7
|
+
import java.sql.ResultSetMetaData;
|
7
8
|
import java.sql.SQLException;
|
8
9
|
import java.sql.Types;
|
9
10
|
|
@@ -11,7 +12,6 @@ import org.jruby.Ruby;
|
|
11
12
|
import org.jruby.RubyArray;
|
12
13
|
import org.jruby.RubyClass;
|
13
14
|
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,17 +21,13 @@ 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
|
-
|
28
24
|
/*
|
29
25
|
* This class mimics the PG:Result class enough to get by
|
30
26
|
*/
|
31
27
|
public class PostgreSQLResult extends JdbcResult {
|
32
28
|
|
33
29
|
// These are needed when generating an AR::Result
|
34
|
-
private final
|
30
|
+
private final ResultSetMetaData resultSetMetaData;
|
35
31
|
|
36
32
|
/********* JRuby compat methods ***********/
|
37
33
|
|
@@ -61,7 +57,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
61
57
|
ResultSet resultSet) throws SQLException {
|
62
58
|
super(context, clazz, connection, resultSet);
|
63
59
|
|
64
|
-
resultSetMetaData =
|
60
|
+
resultSetMetaData = resultSet.getMetaData();
|
65
61
|
}
|
66
62
|
|
67
63
|
/**
|
@@ -74,16 +70,28 @@ public class PostgreSQLResult extends JdbcResult {
|
|
74
70
|
protected IRubyObject columnTypeMap(final ThreadContext context) throws SQLException {
|
75
71
|
Ruby runtime = context.runtime;
|
76
72
|
RubyHash types = RubyHash.newHash(runtime);
|
77
|
-
PgResultSetMetaDataWrapper mdWrapper = new PgResultSetMetaDataWrapper(resultSetMetaData);
|
78
73
|
int columnCount = columnNames.length;
|
79
74
|
|
80
75
|
IRubyObject adapter = connection.adapter(context);
|
81
76
|
for (int i = 0; i < columnCount; i++) {
|
82
|
-
|
77
|
+
int col = i + 1;
|
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
|
+
|
83
91
|
final RubyString name = columnNames[i];
|
84
92
|
final IRubyObject type = Helpers.invoke(context, adapter, "get_oid_type",
|
85
|
-
runtime.
|
86
|
-
runtime.newFixnum(
|
93
|
+
runtime.newString(typeName),
|
94
|
+
runtime.newFixnum(mod),
|
87
95
|
name);
|
88
96
|
|
89
97
|
if (!type.isNil()) types.fastASet(name, type);
|
@@ -144,7 +152,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
144
152
|
* @return ActiveRecord::Result object with the data from this result set
|
145
153
|
* @throws SQLException can be caused by postgres generating its type map
|
146
154
|
*/
|
147
|
-
@Override
|
155
|
+
@Override @SuppressWarnings("unchecked")
|
148
156
|
public IRubyObject toARResult(final ThreadContext context) throws SQLException {
|
149
157
|
RubyClass BinaryDataClass = null;
|
150
158
|
int rowCount = 0;
|
@@ -163,7 +171,7 @@ public class PostgreSQLResult extends JdbcResult {
|
|
163
171
|
RubyArray row = (RubyArray) values.eltInternal(rowIndex);
|
164
172
|
IRubyObject value = row.eltInternal(columnIndex);
|
165
173
|
if (value != context.nil) {
|
166
|
-
row.eltInternalSet(columnIndex,
|
174
|
+
row.eltInternalSet(columnIndex, BinaryDataClass.newInstance(context, value, Block.NULL_BLOCK));
|
167
175
|
}
|
168
176
|
}
|
169
177
|
}
|
@@ -281,6 +281,30 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
281
281
|
return mapExecuteResult(context, connection, resultSet).toARResult(context);
|
282
282
|
}
|
283
283
|
|
284
|
+
@Override
|
285
|
+
protected void setArrayParameter(final ThreadContext context,
|
286
|
+
final Connection connection, final PreparedStatement statement,
|
287
|
+
final int index, final IRubyObject value,
|
288
|
+
final IRubyObject attribute, final int type) throws SQLException {
|
289
|
+
|
290
|
+
final String typeName = resolveArrayBaseTypeName(context, attribute);
|
291
|
+
final RubyArray valueForDB = (RubyArray) value.callMethod(context, "values");
|
292
|
+
|
293
|
+
Object[] values;
|
294
|
+
switch (typeName) {
|
295
|
+
case "datetime":
|
296
|
+
case "timestamp": {
|
297
|
+
values = PgDateTimeUtils.timestampStringArray(context, valueForDB);
|
298
|
+
break;
|
299
|
+
}
|
300
|
+
default:
|
301
|
+
values = valueForDB.toArray();
|
302
|
+
break;
|
303
|
+
}
|
304
|
+
|
305
|
+
statement.setArray(index, connection.createArrayOf(typeName, values));
|
306
|
+
}
|
307
|
+
|
284
308
|
@Override
|
285
309
|
protected void setBlobParameter(final ThreadContext context,
|
286
310
|
final Connection connection, final PreparedStatement statement,
|
@@ -304,47 +328,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
304
328
|
final Connection connection, final PreparedStatement statement,
|
305
329
|
final int index, IRubyObject value,
|
306
330
|
final IRubyObject attribute, final int type) throws SQLException {
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
if ( Double.isInfinite(doubleValue) ) {
|
311
|
-
setTimestampInfinity(statement, index, doubleValue);
|
312
|
-
return;
|
313
|
-
}
|
314
|
-
}
|
315
|
-
|
316
|
-
RubyTime timeValue = toTime(context, value);
|
317
|
-
|
318
|
-
final Timestamp timestamp;
|
319
|
-
|
320
|
-
if (timeValue.getDateTime().getYear() > 0) {
|
321
|
-
timeValue = timeInDefaultTimeZone(context, timeValue);
|
322
|
-
DateTime dateTime = timeValue.getDateTime();
|
323
|
-
timestamp = new Timestamp(dateTime.getMillis());
|
324
|
-
|
325
|
-
if (timeValue.getNSec() > 0) timestamp.setNanos((int) (timestamp.getNanos() + timeValue.getNSec()));
|
326
|
-
|
327
|
-
statement.setTimestamp(index, timestamp, getCalendar(dateTime.getZone()));
|
328
|
-
}
|
329
|
-
else {
|
330
|
-
setTimestampBC(statement, index, timeValue);
|
331
|
-
}
|
332
|
-
}
|
333
|
-
|
334
|
-
private static void setTimestampBC(final PreparedStatement statement,
|
335
|
-
final int index, final RubyTime timeValue) throws SQLException {
|
336
|
-
DateTime dateTime = timeValue.getDateTime();
|
337
|
-
@SuppressWarnings("deprecated")
|
338
|
-
Timestamp timestamp = new Timestamp(dateTime.getYear() - 1900,
|
339
|
-
dateTime.getMonthOfYear() - 1,
|
340
|
-
dateTime.getDayOfMonth(),
|
341
|
-
dateTime.getHourOfDay(),
|
342
|
-
dateTime.getMinuteOfHour(),
|
343
|
-
dateTime.getSecondOfMinute(),
|
344
|
-
dateTime.getMillisOfSecond() * 1_000_000 + (int) timeValue.getNSec()
|
345
|
-
);
|
346
|
-
|
347
|
-
statement.setObject(index, timestamp);
|
331
|
+
// PGJDBC uses strings internally anyway, so using Timestamp doesn't do any good
|
332
|
+
String tsString = PgDateTimeUtils.timestampValueToString(context, value, null, true);
|
333
|
+
statement.setObject(index, tsString, Types.OTHER);
|
348
334
|
}
|
349
335
|
|
350
336
|
private static void setTimestampInfinity(final PreparedStatement statement,
|
@@ -366,7 +352,8 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
366
352
|
final int index, IRubyObject value,
|
367
353
|
final IRubyObject attribute, final int type) throws SQLException {
|
368
354
|
// to handle more fractional second precision than (default) 59.123 only
|
369
|
-
|
355
|
+
String timeStr = DateTimeUtils.timeString(context, value, getDefaultTimeZone(context), true);
|
356
|
+
statement.setObject(index, timeStr, Types.OTHER);
|
370
357
|
}
|
371
358
|
|
372
359
|
@Override
|
@@ -420,8 +407,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
420
407
|
break;
|
421
408
|
|
422
409
|
case "enum":
|
423
|
-
|
424
|
-
statement.setObject(index, value.toString());
|
410
|
+
statement.setObject(index, value.toString(), Types.OTHER);
|
425
411
|
break;
|
426
412
|
|
427
413
|
case "interval":
|