activerecord-jdbc-adapter 51.2-java → 51.3-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -83,8 +83,8 @@ public class DerbyRubyJdbcConnection extends RubyJdbcConnection {
83
83
  final int minor = jdbcDriver.getMinorVersion();
84
84
  if ( major < 10 || ( major == 10 && minor < 5 ) ) {
85
85
  final RubyClass errorClass = getConnectionNotEstablished(context.runtime);
86
- throw new RaiseException(context.runtime, errorClass,
87
- "adapter requires Derby >= 10.5 got: " + major + "." + minor + "", false);
86
+ throw context.runtime.newRaiseException(errorClass,
87
+ "adapter requires Derby >= 10.5 got: " + major + "." + minor + "");
88
88
  }
89
89
  if ( major == 10 && minor < 8 ) { // 10.8 ~ supports JDBC 4.1
90
90
  // config[:connection_alive_sql] ||= 'SELECT 1 FROM SYS.SYSSCHEMAS FETCH FIRST 1 ROWS ONLY'
@@ -27,12 +27,6 @@ package arjdbc.jdbc;
27
27
 
28
28
  import java.sql.Connection;
29
29
  import java.sql.SQLException;
30
- import javax.sql.DataSource;
31
-
32
- import org.jruby.RubyObject;
33
- import org.jruby.RubyString;
34
- import org.jruby.runtime.ThreadContext;
35
- import org.jruby.runtime.builtin.IRubyObject;
36
30
 
37
31
  /**
38
32
  * Interface to be implemented in Ruby for retrieving a new connection.
@@ -49,84 +43,3 @@ public interface ConnectionFactory {
49
43
  Connection newConnection() throws SQLException;
50
44
 
51
45
  }
52
-
53
- class DataSourceConnectionFactoryImpl implements ConnectionFactory {
54
-
55
- private final DataSource dataSource;
56
- final String username, password; // optional
57
-
58
- public DataSourceConnectionFactoryImpl(final DataSource dataSource) {
59
- this.dataSource = dataSource;
60
- this.username = null; this.password = null;
61
- }
62
-
63
- public DataSourceConnectionFactoryImpl(final DataSource dataSource,
64
- final String username, final String password) {
65
- this.dataSource = dataSource;
66
- this.username = username; this.password = password;
67
- }
68
-
69
- @Override
70
- public Connection newConnection() throws SQLException {
71
- if ( username != null ) {
72
- dataSource.getConnection(username, password);
73
- }
74
- return dataSource.getConnection();
75
- }
76
-
77
- DataSource getDataSource() { return dataSource; } /* for tests */
78
-
79
- }
80
-
81
- class DriverConnectionFactoryImpl implements ConnectionFactory {
82
-
83
- private final DriverWrapper driverWrapper;
84
- final String url;
85
- final String username, password; // null allowed
86
-
87
- public DriverConnectionFactoryImpl(final DriverWrapper driver, final String url) {
88
- this.driverWrapper = driver; this.url = url;
89
- this.username = null; this.password = null;
90
- }
91
-
92
- public DriverConnectionFactoryImpl(final DriverWrapper driver, final String url,
93
- final String username, final String password) {
94
- this.driverWrapper = driver; this.url = url;
95
- this.username = username; this.password = password;
96
- }
97
-
98
- @Override
99
- public Connection newConnection() throws SQLException {
100
- return driverWrapper.connect(url, username, password);
101
- }
102
-
103
- DriverWrapper getDriverWrapper() { return driverWrapper; } /* for tests */
104
-
105
- }
106
-
107
- // @legacy ActiveRecord::ConnectionAdapters::JdbcDriver
108
- class RubyConnectionFactoryImpl implements ConnectionFactory {
109
-
110
- private final IRubyObject driver;
111
- final RubyString url;
112
- final IRubyObject username, password; // null allowed
113
-
114
- private final RubyObject contextProvider;
115
-
116
- public RubyConnectionFactoryImpl(final IRubyObject driver, final RubyString url,
117
- final IRubyObject username, final IRubyObject password) {
118
- this.driver = driver; this.url = url;
119
- this.username = username; this.password = password;
120
- contextProvider = (RubyObject) driver;
121
- }
122
-
123
- @Override
124
- public Connection newConnection() throws SQLException {
125
- final ThreadContext context = contextProvider.getRuntime().getCurrentContext();
126
- final IRubyObject connection = driver.callMethod(context, "connection", new IRubyObject[] { url, username, password });
127
- return (Connection) connection.toJava(Connection.class);
128
- }
129
-
130
- IRubyObject getDriver() { return driver; } /* for tests */
131
-
132
- }
@@ -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 new RaiseException(runtime, errorClass, "adapter requires :driver class and jdbc :url", false);
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 RubyConnectionFactoryImpl(
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(); }
@@ -2687,7 +2712,7 @@ public class RubyJdbcConnection extends RubyObject {
2687
2712
  }
2688
2713
 
2689
2714
  protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
2690
- return timeInDefaultTimeZone(context, toTime(context, value));
2715
+ return timeInDefaultTimeZone(context, DateTimeUtils.toTime(context, value));
2691
2716
  }
2692
2717
 
2693
2718
  protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final RubyTime time) {
@@ -2699,11 +2724,14 @@ public class RubyJdbcConnection extends RubyObject {
2699
2724
  return timeInDefaultTZ;
2700
2725
  }
2701
2726
 
2727
+ protected final DateTime dateTimeInDefaultTimeZone(final ThreadContext context, final DateTime dateTime) {
2728
+ final DateTimeZone defaultZone = getDefaultTimeZone(context);
2729
+ if (defaultZone == dateTime.getZone()) return dateTime;
2730
+ return dateTime.withZone(defaultZone);
2731
+ }
2732
+
2702
2733
  public static RubyTime toTime(final ThreadContext context, final IRubyObject value) {
2703
- if ( ! ( value instanceof RubyTime ) ) { // unlikely
2704
- return (RubyTime) TypeConverter.convertToTypeWithCheck(value, context.runtime.getTime(), "to_time");
2705
- }
2706
- return (RubyTime) value;
2734
+ return DateTimeUtils.toTime(context, value);
2707
2735
  }
2708
2736
 
2709
2737
  protected boolean isDefaultTimeZoneUTC(final ThreadContext context) {
@@ -2803,8 +2831,8 @@ public class RubyJdbcConnection extends RubyObject {
2803
2831
  final int index, IRubyObject value,
2804
2832
  final IRubyObject attribute, final int type) throws SQLException {
2805
2833
 
2806
- final RubyTime timeValue = timeInDefaultTimeZone(context, value);
2807
- final DateTime dateTime = timeValue.getDateTime();
2834
+ final RubyTime timeValue = DateTimeUtils.toTime(context, value);
2835
+ final DateTime dateTime = dateTimeInDefaultTimeZone(context, timeValue.getDateTime());
2808
2836
  final Timestamp timestamp = new Timestamp(dateTime.getMillis());
2809
2837
  // 1942-11-30T01:02:03.123_456
2810
2838
  if (timeValue.getNSec() > 0) timestamp.setNanos((int) (timestamp.getNanos() + timeValue.getNSec()));
@@ -2898,7 +2926,7 @@ public class RubyJdbcConnection extends RubyObject {
2898
2926
  final RubySymbol type = (RubySymbol) attributeSQLType(context, attribute);
2899
2927
 
2900
2928
  // For some reason the driver doesn't like "character varying" as a type
2901
- if ( type.eql(context.runtime.newSymbol("string")) ) return "text";
2929
+ if ( type.eql(context.runtime.newSymbol("string")) ) return "varchar";
2902
2930
 
2903
2931
  final RubyHash nativeTypes = (RubyHash) getAdapter().callMethod(context, "native_database_types");
2904
2932
  // e.g. `integer: { name: 'integer' }`
@@ -3005,7 +3033,7 @@ public class RubyJdbcConnection extends RubyObject {
3005
3033
  private void handleNotConnected() {
3006
3034
  final Ruby runtime = getRuntime();
3007
3035
  final RubyClass errorClass = getConnectionNotEstablished( runtime );
3008
- throw new RaiseException(runtime, errorClass, "no connection available", false);
3036
+ throw runtime.newRaiseException(errorClass, "no connection available");
3009
3037
  }
3010
3038
 
3011
3039
  /**
@@ -3542,7 +3570,7 @@ public class RubyJdbcConnection extends RubyObject {
3542
3570
  return (RaiseException) exception;
3543
3571
  }
3544
3572
  if ( exception instanceof RuntimeException ) {
3545
- return RaiseException.createNativeRaiseException(runtime, exception);
3573
+ return wrapException(context, context.runtime.getRuntimeError(), exception);
3546
3574
  }
3547
3575
  // NOTE: compat - maybe makes sense or maybe not (e.g. IOException) :
3548
3576
  return wrapException(context, getJDBCError(runtime), exception);
@@ -3555,7 +3583,7 @@ public class RubyJdbcConnection extends RubyObject {
3555
3583
 
3556
3584
  public static RaiseException wrapException(final ThreadContext context,
3557
3585
  final RubyClass errorClass, final Throwable exception, final String message) {
3558
- final RaiseException error = new RaiseException(context.runtime, errorClass, message, true);
3586
+ final RaiseException error = context.runtime.newRaiseException(errorClass, message);
3559
3587
  error.initCause(exception);
3560
3588
  return error;
3561
3589
  }
@@ -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 new RaiseException(context.runtime, errorClass,
111
- "MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "", false);
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 PgResultSetMetaData resultSetMetaData;
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 = (PgResultSetMetaData) resultSet.getMetaData();
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
- final Field field = mdWrapper.getField(i + 1);
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.newFixnum(field.getOID()),
86
- runtime.newFixnum(field.getMod()),
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, (IRubyObject) BinaryDataClass.newInstance(context, value, Block.NULL_BLOCK));
174
+ row.eltInternalSet(columnIndex, BinaryDataClass.newInstance(context, value, Block.NULL_BLOCK));
167
175
  }
168
176
  }
169
177
  }