Skip to content

Commit 939e798

Browse files
committed
Merge branch '51-stable' into 52-stable
2 parents 1595848 + 1e20602 commit 939e798

File tree

9 files changed

+128
-48
lines changed

9 files changed

+128
-48
lines changed

lib/arjdbc/postgresql/oid_types.rb

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,61 @@ module PostgreSQL
99
# @private
1010
OID = ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID
1111

12+
# this version makes sure to register the types by name as well
13+
# we still need to version with OID since it's used from SchemaStatements as well
14+
class ArjdbcTypeMapInitializer < OID::TypeMapInitializer
15+
private
16+
17+
def name_with_ns(row)
18+
if row['in_ns']
19+
row['typname']
20+
else
21+
%Q("#{row['nspname']}"."#{row['typname']}")
22+
end
23+
end
24+
25+
def register_enum_type(row)
26+
super
27+
register name_with_ns(row), OID::Enum.new
28+
end
29+
30+
def register_array_type(row)
31+
super
32+
register_with_subtype(name_with_ns(row), row['typelem'].to_i) do |subtype|
33+
OID::Array.new(subtype, row['typdelim'])
34+
end
35+
end
36+
37+
def register_range_type(row)
38+
super
39+
name = name_with_ns(row)
40+
register_with_subtype(name, row['rngsubtype'].to_i) do |subtype|
41+
OID::Range.new(subtype, name.to_sym)
42+
end
43+
end
44+
45+
def register_domain_type(row)
46+
if base_type = @store.lookup(row['typbasetype'].to_i)
47+
register row['oid'], base_type
48+
register name_with_ns(row), base_type
49+
else
50+
warn "unknown base type (OID: #{row['typbasetype']}) for domain #{row['typname']}."
51+
end
52+
end
53+
54+
def register_composite_type(row)
55+
if subtype = @store.lookup(row['typelem'].to_i)
56+
register row['oid'], OID::Vector.new(row['typdelim'], subtype)
57+
register name_with_ns(row), OID::Vector.new(row['typdelim'], subtype)
58+
end
59+
end
60+
61+
def assert_valid_registration(oid, oid_type)
62+
ret = super
63+
ret == 0 ? oid : ret
64+
end
65+
end
66+
1267
# @private
1368
module OIDTypes
1469

@@ -126,27 +181,46 @@ def initialize_type_map(m = type_map)
126181
end
127182
end
128183

129-
load_additional_types
184+
load_additional_types(m)
185+
186+
# pgjdbc returns these if the column is auto-incrmenting
187+
m.alias_type 'serial', 'int4'
188+
m.alias_type 'bigserial', 'int8'
130189
end
131190

132-
def load_additional_types(oids = nil) # :nodoc:
133-
initializer = OID::TypeMapInitializer.new(type_map)
191+
def load_additional_types(type_map, oid = nil) # :nodoc:
192+
initializer = ArjdbcTypeMapInitializer.new(type_map)
134193

135194
if supports_ranges?
136195
query = <<-SQL
137-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
196+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype,
197+
ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
138198
FROM pg_type as t
139199
LEFT JOIN pg_range as r ON oid = rngtypid
200+
JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
140201
SQL
141202
else
142203
query = <<-SQL
143-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
204+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype,
205+
ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
144206
FROM pg_type as t
207+
JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
145208
SQL
146209
end
147210

148-
if oids
149-
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
211+
if oid
212+
if oid.is_a? Numeric || oid.match(/^\d+$/)
213+
# numeric OID
214+
query += "WHERE t.oid::integer = %s" % oid
215+
216+
elsif m = oid.match(/"?(\w+)"?\."?(\w+)"?/)
217+
# namespace and type name
218+
query += "WHERE ns.nspname = '%s' AND t.typname = '%s'" % [m[1], m[2]]
219+
220+
else
221+
# only type name
222+
query += "WHERE t.typname = '%s' AND ns.nspname = ANY(current_schemas(true))" % oid
223+
end
150224
else
151225
query += initializer.query_conditions_for_initial_load
152226
end

src/java/arjdbc/jdbc/RubyJdbcConnection.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public class RubyJdbcConnection extends RubyObject {
128128
private boolean lazy = false; // final once set on initialize
129129
private boolean jndi; // final once set on initialize
130130
private boolean configureConnection = true; // final once initialized
131+
private int fetchSize = 0; // 0 = JDBC default
131132

132133
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
133134
super(runtime, metaClass);
@@ -571,6 +572,11 @@ protected void doInitialize(final ThreadContext context, final IRubyObject confi
571572
else {
572573
this.configureConnection = value != context.runtime.getFalse();
573574
}
575+
576+
IRubyObject jdbcFetchSize = getConfigValue(context, "jdbc_fetch_size");
577+
if (jdbcFetchSize != context.nil) {
578+
this.fetchSize = RubyNumeric.fix2int(jdbcFetchSize);
579+
}
574580
}
575581

576582
@JRubyMethod(name = "adapter")
@@ -818,6 +824,7 @@ public IRubyObject call(final Connection connection) throws SQLException {
818824
// is called, so we have to process the result sets as we get them
819825
// this shouldn't be an issue in most cases since we're only getting 1 result set anyways
820826
result = mapExecuteResult(context, connection, resultSet);
827+
resultSet.close();
821828
} else {
822829
result = context.runtime.newFixnum(updateCount);
823830
}
@@ -851,6 +858,7 @@ protected Statement createStatement(final ThreadContext context, final Connectio
851858
else {
852859
statement.setEscapeProcessing(escapeProcessing.isTrue());
853860
}
861+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
854862
return statement;
855863
}
856864

@@ -1045,6 +1053,7 @@ public IRubyObject call(final Connection connection) throws SQLException {
10451053
else {
10461054
final PreparedStatement prepStatement;
10471055
statement = prepStatement = connection.prepareStatement(query);
1056+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
10481057
statement.setMaxRows(maxRows); // zero means there is no limit
10491058
setStatementParameters(context, connection, prepStatement, binds);
10501059
hasResult = prepStatement.execute();
@@ -1123,7 +1132,9 @@ public IRubyObject prepare_statement(final ThreadContext context, final IRubyObj
11231132
return withConnection(context, new Callable<IRubyObject>() {
11241133
public IRubyObject call(Connection connection) throws SQLException {
11251134
final String query = sql.convertToString().getUnicodeValue();
1126-
return JavaUtil.convertJavaToRuby(context.runtime, connection.prepareStatement(query));
1135+
PreparedStatement statement = connection.prepareStatement(query);
1136+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
1137+
return JavaUtil.convertJavaToRuby(context.runtime, statement);
11271138
}
11281139
});
11291140
}
@@ -1158,19 +1169,15 @@ public IRubyObject call(final Connection connection) throws SQLException {
11581169
statement = (PreparedStatement) JavaEmbedUtils.rubyToJava(cachedStatement);
11591170
} else {
11601171
statement = connection.prepareStatement(query);
1172+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
11611173
}
11621174

11631175
setStatementParameters(context, connection, statement, (RubyArray) binds);
11641176

11651177
if (statement.execute()) {
11661178
ResultSet resultSet = statement.getResultSet();
11671179
IRubyObject results = mapQueryResult(context, connection, resultSet);
1168-
1169-
if (cached) {
1170-
// Make sure we free the result set if we are caching the statement
1171-
// It gets closed automatically when the statement is closed if we aren't caching
1172-
resultSet.close();
1173-
}
1180+
resultSet.close();
11741181

11751182
return results;
11761183
} else {
@@ -2501,6 +2508,8 @@ protected IRubyObject arrayToRuby(final ThreadContext context,
25012508
while ( arrayResult.next() ) {
25022509
array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
25032510
}
2511+
arrayResult.close();
2512+
25042513
return array;
25052514
}
25062515
finally { if ( value != null ) value.free(); }

src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/java/arjdbc/postgresql/PostgreSQLResult.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import arjdbc.jdbc.RubyJdbcConnection;
55

66
import java.sql.ResultSet;
7+
import java.sql.ResultSetMetaData;
78
import java.sql.SQLException;
89
import java.sql.Types;
910

@@ -22,10 +23,6 @@
2223
import org.jruby.runtime.ThreadContext;
2324
import org.jruby.runtime.builtin.IRubyObject;
2425

25-
import org.postgresql.core.Field;
26-
import org.postgresql.jdbc.PgResultSetMetaData;
27-
import org.postgresql.jdbc.PgResultSetMetaDataWrapper; // This is a hack unfortunately to get around method scoping
28-
2926
/*
3027
* This class mimics the PG::Result class enough to get by. It also adorns common methods useful for
3128
* gems like mini_sql to consume it similarly to PG::Result
@@ -34,7 +31,7 @@ public class PostgreSQLResult extends JdbcResult {
3431
private RubyArray fields = null; // lazily created if PG fields method is called.
3532

3633
// These are needed when generating an AR::Result
37-
private final PgResultSetMetaData resultSetMetaData;
34+
private final ResultSetMetaData resultSetMetaData;
3835

3936
/********* JRuby compat methods ***********/
4037

@@ -64,7 +61,7 @@ private PostgreSQLResult(ThreadContext context, RubyClass clazz, RubyJdbcConnect
6461
ResultSet resultSet) throws SQLException {
6562
super(context, clazz, connection, resultSet);
6663

67-
resultSetMetaData = (PgResultSetMetaData) resultSet.getMetaData();
64+
resultSetMetaData = resultSet.getMetaData();
6865
}
6966

7067
/**
@@ -77,16 +74,28 @@ private PostgreSQLResult(ThreadContext context, RubyClass clazz, RubyJdbcConnect
7774
protected IRubyObject columnTypeMap(final ThreadContext context) throws SQLException {
7875
Ruby runtime = context.runtime;
7976
RubyHash types = RubyHash.newHash(runtime);
80-
PgResultSetMetaDataWrapper mdWrapper = new PgResultSetMetaDataWrapper(resultSetMetaData);
8177
int columnCount = columnNames.length;
8278

8379
IRubyObject adapter = connection.adapter(context);
8480
for (int i = 0; i < columnCount; i++) {
85-
final Field field = mdWrapper.getField(i + 1);
81+
int col = i + 1;
82+
String typeName = resultSetMetaData.getColumnTypeName(col);
83+
84+
int mod = 0;
85+
if ("numeric".equals(typeName)) {
86+
// this field is only relevant for "numeric" type in AR
87+
// AR checks (fmod - 4 & 0xffff).zero?
88+
// pgjdbc:
89+
// - for typmod == -1, getScale() and getPrecision() return 0
90+
// - for typmod != -1, getScale() returns "(typmod - 4) & 0xFFFF;"
91+
mod = resultSetMetaData.getScale(col);
92+
mod = mod == 0 && resultSetMetaData.getPrecision(col) == 0 ? -1 : mod + 4;
93+
}
94+
8695
final RubyString name = columnNames[i];
8796
final IRubyObject type = Helpers.invoke(context, adapter, "get_oid_type",
88-
runtime.newFixnum(field.getOID()),
89-
runtime.newFixnum(field.getMod()),
97+
runtime.newString(typeName),
98+
runtime.newFixnum(mod),
9099
name);
91100

92101
if (!type.isNil()) types.fastASet(name, type);

test/rails/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ connections:
6262
driver: <%= 'org.mariadb.jdbc.Driver' %>
6363
<% end %>
6464
<% if defined? JRUBY_VERSION %>
65+
jdbc_fetch_size: 100
6566
properties:
6667
serverTimezone: <%= java.util.TimeZone.getDefault.getID %>
6768
<% end %>
@@ -80,6 +81,7 @@ connections:
8081
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || ENV['PS'] %> # should leave it off to its default if not set
8182
<% end %>
8283
username: <%= ENV['PGUSER'] || ENV['user'] || 'rails' %>
84+
jdbc_fetch_size: 100
8385
properties:
8486
# Postgres' JDBC driver does not prepare statements until executed 5 times by default, let's lower:
8587
prepareThreshold: <%= ENV['PREPARE_THRESHOLD'] || 1 %>
@@ -99,9 +101,11 @@ connections:
99101
arunit:
100102
database: <%= FIXTURES_ROOT %>/fixture_database.sqlite3
101103
timeout: 5000
104+
jdbc_fetch_size: 100
102105
arunit2:
103106
database: <%= FIXTURES_ROOT %>/fixture_database_2.sqlite3
104107
timeout: 5000
108+
jdbc_fetch_size: 100
105109

106110
sqlite3_mem:
107111
arunit:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
exclude :test_some_time, 'intermittent failures, leaks thread, fires at high frequency'
2+
exclude :test_connection_pool_starts_reaper, 'intermittent failures, leaks thread, fires at high frequency'

test/rails/excludes/postgresql/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterTest.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
exclude :test_exec_typecasts_bind_vals, 'it uses $1 for parameter mapping which is not currently supported'
44
end
55

6+
exclude :test_only_warn_on_first_encounter_of_unrecognized_oid, 'expects warning with OID, ARJBC has name instead'
7+
exclude :test_only_warn_on_first_encounter_of_unknown_oid, 'expects warning with OID, ARJBC has name instead'
68
exclude :test_default_sequence_name_bad_table, "ARJDBC does more quoting (which is not wrong)"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
exclude :test_some_time, 'intermittent failures, leaks thread, fires at high frequency'
2+
exclude :test_connection_pool_starts_reaper, 'intermittent failures, leaks thread, fires at high frequency'
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
exclude :test_connection_pool_starts_reaper, "Hangs due to threading code in test"
1+
exclude :test_some_time, 'intermittent failures, leaks thread, fires at high frequency'
2+
exclude :test_connection_pool_starts_reaper, 'intermittent failures, leaks thread, fires at high frequency'

0 commit comments

Comments
 (0)