activerecord-jdbc-adapter 1.3.5 → 1.3.6
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/.travis.yml +1 -2
- data/Gemfile +2 -2
- data/History.md +13 -1
- data/lib/arjdbc/derby/active_record_patch.rb +3 -2
- data/lib/arjdbc/derby/adapter.rb +3 -1
- data/lib/arjdbc/jdbc/adapter.rb +2 -2
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/mssql/adapter.rb +27 -1
- data/lib/arjdbc/mssql/limit_helpers.rb +22 -1
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +21 -14
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +42 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +39 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebdc2f21df1a1fbe848c242a5a9c6e351a20056e
|
4
|
+
data.tar.gz: 67e764365675757678fc29d3a2422faaa0b2920a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 006bb63ddde830d1b02a26c5780349d1ad7e6204dbcd9195583fcf65b11f102a22e4e9dab43a6e242c344293503dd56140b6fa55f7b48e967289785b74bec88a
|
7
|
+
data.tar.gz: e9e05577e5fb7965d0a5b38b64f1b7985d9412a31f5d0c7d414a4e8c1b865293ca8e77c120ee7402b7088c70c4a0b9c3f93a07c77868cde11a27ed9406b61e52
|
data/.travis.yml
CHANGED
@@ -3,8 +3,7 @@ bundler_args: --without development
|
|
3
3
|
script: bundle exec rake test_$DB
|
4
4
|
before_script: export JRUBY_OPTS="--server -Xcext.enabled=false -Xcompile.invokedynamic=false"
|
5
5
|
before_install:
|
6
|
-
|
7
|
-
#- 'ruby -e "puts RUBY_VERSION" | grep ^1\.8 && gem update --system -v 2.1.11 || true'
|
6
|
+
- jruby --1.9 -S gem update --system 2.1.11
|
8
7
|
rvm:
|
9
8
|
- jruby
|
10
9
|
gemfile:
|
data/Gemfile
CHANGED
@@ -20,8 +20,8 @@ gem 'appraisal', :require => nil
|
|
20
20
|
|
21
21
|
# appraisal ignores group block declarations :
|
22
22
|
|
23
|
-
gem 'test-unit', '2.5.4', :group => :test
|
24
|
-
gem 'test-unit-context', '>= 0.
|
23
|
+
gem 'test-unit', '~> 2.5.4', :group => :test
|
24
|
+
gem 'test-unit-context', '>= 0.4.0', :group => :test
|
25
25
|
gem 'mocha', '~> 0.13.1', :require => nil, :group => :test
|
26
26
|
|
27
27
|
gem 'simplecov', :require => nil, :group => :test
|
data/History.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 1.3.6 (02/04/14)
|
2
|
+
|
3
|
+
- fix rails 4-0-stable compatibility (see #530)
|
4
|
+
- [mysql] support "disabling" abandoned connection cleanup thread
|
5
|
+
- [mssql] Handling of 'GROUP BY' and selected columns (#529)
|
6
|
+
+ SELECT DISTINCT clause with ORDER BY for MSSQL (partially fixes #437)
|
7
|
+
- [derby] only do the patched select_limited_ids if connection is Derby's
|
8
|
+
- [derby] support getting and setting transaction isolation on a connection
|
9
|
+
+ allow to configure whether isolation will be 'serializable' (work-around for #497)
|
10
|
+
- match 'int' as well as 'integer' when converting to SQL types for MSSQL (#527)
|
11
|
+
|
12
|
+
Code Contributors: Sean McCarthy, Jesko, Konstantin Shabanov
|
13
|
+
|
1
14
|
## 1.3.5 (01/10/14)
|
2
15
|
|
3
16
|
We're now green against Rails 4.1 (master), test and report issues if any.
|
@@ -8,7 +21,6 @@ We're now green against Rails 4.1 (master), test and report issues if any.
|
|
8
21
|
- [firebird] Insert quotes for blobs to prevent failed inserts on not-null cols
|
9
22
|
|
10
23
|
Code Contributors: Ray Zane, Gary S. Weaver
|
11
|
-
Code Contributors: @rzane, @garysweaver
|
12
24
|
|
13
25
|
## 1.3.4 (12/12/13)
|
14
26
|
|
@@ -1,9 +1,10 @@
|
|
1
|
-
# Needed because Rails is broken wrt to quoting of some values.
|
2
|
-
# Most databases are nice about it, but not Derby.
|
1
|
+
# Needed because Rails is broken wrt to quoting of some values.
|
2
|
+
# Most databases are nice about it, but not Derby.
|
3
3
|
# The real issue is that you can't compare a CHAR value to a NUMBER column.
|
4
4
|
ActiveRecord::Associations::ClassMethods.module_eval do
|
5
5
|
private
|
6
6
|
def select_limited_ids_list(options, join_dependency)
|
7
|
+
return super unless connection.is_a?(ArJdbc::Derby)
|
7
8
|
connection.select_all(
|
8
9
|
construct_finder_sql_for_association_limiting(options, join_dependency),
|
9
10
|
"#{name} Load IDs For Limited Eager Loading"
|
data/lib/arjdbc/derby/adapter.rb
CHANGED
@@ -109,7 +109,9 @@ module ArJdbc
|
|
109
109
|
|
110
110
|
def configure_connection
|
111
111
|
# must be done or SELECT...FOR UPDATE won't work how we expect :
|
112
|
-
|
112
|
+
tx_isolation = config[:transaction_isolation] # set false to leave as is
|
113
|
+
tx_isolation = :serializable if tx_isolation.nil?
|
114
|
+
@connection.transaction_isolation = tx_isolation if tx_isolation
|
113
115
|
# if a user name was specified upon connection, the user's name is the
|
114
116
|
# default schema for the connection, if a schema with that name exists
|
115
117
|
set_schema(config[:schema]) if config.key?(:schema)
|
data/lib/arjdbc/jdbc/adapter.rb
CHANGED
@@ -507,8 +507,8 @@ module ActiveRecord
|
|
507
507
|
|
508
508
|
# @private
|
509
509
|
# @override
|
510
|
-
def select_rows(sql, name = nil)
|
511
|
-
exec_query_raw(sql, name).map!(&:values)
|
510
|
+
def select_rows(sql, name = nil, binds = [])
|
511
|
+
exec_query_raw(sql, name, binds).map!(&:values)
|
512
512
|
end
|
513
513
|
|
514
514
|
if ActiveRecord::VERSION::MAJOR > 3 # expects AR::Result e.g. from select_all
|
Binary file
|
data/lib/arjdbc/mssql/adapter.rb
CHANGED
@@ -123,7 +123,7 @@ module ArJdbc
|
|
123
123
|
'NVARCHAR(MAX)'
|
124
124
|
elsif NO_LIMIT_TYPES.include?(type_s)
|
125
125
|
super(type)
|
126
|
-
elsif type_s == 'integer'
|
126
|
+
elsif type_s == 'integer' || type_s == 'int'
|
127
127
|
if limit.nil? || limit == 4
|
128
128
|
'int'
|
129
129
|
elsif limit == 2
|
@@ -360,6 +360,32 @@ module ArJdbc
|
|
360
360
|
execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
|
361
361
|
end
|
362
362
|
|
363
|
+
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
364
|
+
# MSSQL requires the ORDER BY columns in the select list for distinct queries.
|
365
|
+
def distinct(columns, order_by)
|
366
|
+
"DISTINCT #{columns_for_distinct(columns, order_by)}"
|
367
|
+
end
|
368
|
+
|
369
|
+
def columns_for_distinct(columns, orders)
|
370
|
+
return columns if orders.blank?
|
371
|
+
|
372
|
+
# construct a clean list of column names from the ORDER BY clause,
|
373
|
+
# removing any ASC/DESC modifiers
|
374
|
+
order_columns = [ orders ]; order_columns.flatten! # AR 3.x vs 4.x
|
375
|
+
order_columns.map! do |column|
|
376
|
+
column = column.to_sql unless column.is_a?(String) # handle AREL node
|
377
|
+
column.split(',').collect!{ |s| s.split.first }
|
378
|
+
end.flatten!
|
379
|
+
order_columns.reject!(&:blank?)
|
380
|
+
order_columns = order_columns.zip(0...order_columns.size).to_a
|
381
|
+
order_columns = order_columns.map{ |s, i| "#{s}" }
|
382
|
+
|
383
|
+
columns = [ columns ]; columns.flatten!
|
384
|
+
columns.push( *order_columns ).join(', ')
|
385
|
+
# return a DISTINCT clause that's distinct on the columns we want but
|
386
|
+
# includes all the required columns for the ORDER BY to work properly
|
387
|
+
end
|
388
|
+
|
363
389
|
# @override
|
364
390
|
def change_column(table_name, column_name, type, options = {})
|
365
391
|
|
@@ -24,13 +24,34 @@ module ArJdbc
|
|
24
24
|
rest_of_query = "#{from_table}.#{rest_of_query}"
|
25
25
|
end
|
26
26
|
|
27
|
+
# Ensure correct queries if the rest_of_query contains a 'GROUP BY'. Otherwise the following error occurs:
|
28
|
+
# ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Column 'users.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
|
29
|
+
# SELECT t.* FROM ( SELECT ROW_NUMBER() OVER(ORDER BY users.id) AS _row_num, [users].[lft], COUNT([users].[lft]) FROM [users] GROUP BY [users].[lft] HAVING COUNT([users].[lft]) > 1 ) AS t WHERE t._row_num BETWEEN 1 AND 1
|
30
|
+
if rest_of_query.downcase.include?('group by')
|
31
|
+
if order.count(',') == 0
|
32
|
+
order.gsub!(/ORDER BY (.*)/, 'ORDER BY MIN(\1)')
|
33
|
+
else
|
34
|
+
raise('Only one order condition allowed.')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
27
38
|
if distinct # select =~ /DISTINCT/i
|
28
39
|
order = order.gsub(/([a-z0-9_])+\./, 't.')
|
29
40
|
new_sql = "SELECT t.* FROM "
|
30
41
|
new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, t.* FROM (#{select} #{rest_of_query}) AS t ) AS t"
|
31
42
|
new_sql << " WHERE t._row_num BETWEEN #{start_row} AND #{end_row}"
|
32
43
|
else
|
33
|
-
|
44
|
+
select_columns_before_from = rest_of_query.gsub(/FROM.*/, '').strip
|
45
|
+
only_one_column = !select_columns_before_from.include?(',')
|
46
|
+
only_one_id_column = only_one_column && (select_columns_before_from.ends_with?('.id') || select_columns_before_from.ends_with?('.[id]'))
|
47
|
+
|
48
|
+
if only_one_id_column
|
49
|
+
# If there's only one id column a subquery will be created which only contains this column
|
50
|
+
new_sql = "#{select} t.id FROM "
|
51
|
+
else
|
52
|
+
# All selected columns are used
|
53
|
+
new_sql = "#{select} t.* FROM "
|
54
|
+
end
|
34
55
|
new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query} ) AS t"
|
35
56
|
new_sql << " WHERE t._row_num BETWEEN #{start_row} AND #{end_row}"
|
36
57
|
end
|
data/lib/arjdbc/version.rb
CHANGED
data/rakelib/02-test.rake
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
require File.expand_path('../../test/shared_helper', __FILE__)
|
2
2
|
|
3
|
+
test_tasks = [ 'test_mysql', 'test_sqlite3', 'test_postgresql_with_hint' ]
|
3
4
|
if defined?(JRUBY_VERSION)
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
test_tasks.push :test_derby, :test_hsqldb, :test_h2
|
6
|
+
test_tasks.push :test_jndi, :test_jdbc
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Run \"most\" available test_xxx tasks"
|
10
|
+
task :test => test_tasks
|
11
|
+
|
12
|
+
task 'test_postgresql_with_hint' do
|
13
|
+
if PostgresHelper.have_postgres?(false)
|
14
|
+
Rake::Task['test_postgresql'].invoke
|
15
|
+
else
|
16
|
+
puts "NOTE: won't run test_postgresql"
|
12
17
|
end
|
13
|
-
else
|
14
|
-
task :test => [ :test_mysql ]
|
15
18
|
end
|
16
19
|
|
17
20
|
def set_test_task_compat_version(task)
|
@@ -26,14 +29,18 @@ def set_task_description(task, desc)
|
|
26
29
|
task = task.name if task.is_a?(Rake::TestTask)
|
27
30
|
task = Rake::Task[task]
|
28
31
|
end
|
29
|
-
# reset the
|
30
|
-
task.
|
32
|
+
# reset the description set-up by TestTask :
|
33
|
+
if task.instance_variable_defined? :@full_comment
|
34
|
+
task.instance_variable_set(:@full_comment, nil)
|
35
|
+
else
|
36
|
+
task.instance_variable_get(:@comments).clear
|
37
|
+
end
|
31
38
|
task.add_description(desc)
|
32
39
|
end
|
33
40
|
|
34
41
|
task 'test_appraisal_hint' do
|
35
42
|
next if File.exists?('.disable-appraisal-hint')
|
36
|
-
unless (ENV['BUNDLE_GEMFILE']
|
43
|
+
unless (ENV['BUNDLE_GEMFILE'] || '') =~ /gemfiles\/.*?\.gemfile/
|
37
44
|
appraisals = []; Appraisal::File.each { |file| appraisals << file.name }
|
38
45
|
puts "HINT: specify AR version with `rake appraisal:{version} test_{adapter}'" +
|
39
46
|
" where version=(#{appraisals.join('|')}) (`touch .disable-appraisal-hint' to disable)"
|
@@ -80,8 +87,8 @@ test_task_for :MSSQL, :driver => :jtds, :database_name => 'MS-SQL (SQLServer)'
|
|
80
87
|
test_task_for :MySQL, :prereqs => 'db:mysql'
|
81
88
|
test_task_for :PostgreSQL, :prereqs => 'db:postgresql', :driver => 'postgres'
|
82
89
|
task :test_postgres => :test_postgresql # alias
|
83
|
-
task :test_pgsql => :test_postgresql # alias
|
84
90
|
test_task_for :SQLite3
|
91
|
+
task :test_sqlite => :test_sqlite3 # alias
|
85
92
|
test_task_for :Firebird
|
86
93
|
|
87
94
|
# ensure driver for these DBs is on your class-path
|
@@ -118,4 +118,46 @@ public class DerbyRubyJdbcConnection extends RubyJdbcConnection {
|
|
118
118
|
return super.matchTables(runtime, connection, catalog, schemaPattern, tablePattern, types, checkExistsOnly);
|
119
119
|
}
|
120
120
|
|
121
|
+
@JRubyMethod(name = "transaction_isolation", alias = "get_transaction_isolation")
|
122
|
+
public IRubyObject get_transaction_isolation(final ThreadContext context) {
|
123
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
124
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
125
|
+
final int level = connection.getTransactionIsolation();
|
126
|
+
final String isolationSymbol = formatTransactionIsolationLevel(level);
|
127
|
+
if ( isolationSymbol == null ) return context.getRuntime().getNil();
|
128
|
+
return context.getRuntime().newSymbol(isolationSymbol);
|
129
|
+
}
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
133
|
+
@JRubyMethod(name = "transaction_isolation=", alias = "set_transaction_isolation")
|
134
|
+
public IRubyObject set_transaction_isolation(final ThreadContext context, final IRubyObject isolation) {
|
135
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
136
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
137
|
+
final int level;
|
138
|
+
if ( isolation.isNil() ) {
|
139
|
+
level = connection.getMetaData().getDefaultTransactionIsolation();
|
140
|
+
}
|
141
|
+
else {
|
142
|
+
level = mapTransactionIsolationLevel(isolation);
|
143
|
+
}
|
144
|
+
|
145
|
+
connection.setTransactionIsolation(level);
|
146
|
+
|
147
|
+
final String isolationSymbol = formatTransactionIsolationLevel(level);
|
148
|
+
if ( isolationSymbol == null ) return context.getRuntime().getNil();
|
149
|
+
return context.getRuntime().newSymbol(isolationSymbol);
|
150
|
+
}
|
151
|
+
});
|
152
|
+
}
|
153
|
+
|
154
|
+
public static String formatTransactionIsolationLevel(final int level) {
|
155
|
+
if ( level == Connection.TRANSACTION_READ_UNCOMMITTED ) return "read_uncommitted"; // 1
|
156
|
+
if ( level == Connection.TRANSACTION_READ_COMMITTED ) return "read_committed"; // 2
|
157
|
+
if ( level == Connection.TRANSACTION_REPEATABLE_READ ) return "repeatable_read"; // 4
|
158
|
+
if ( level == Connection.TRANSACTION_SERIALIZABLE ) return "serializable"; // 8
|
159
|
+
if ( level == 0 ) return null;
|
160
|
+
throw new IllegalArgumentException("unexpected transaction isolation level: " + level);
|
161
|
+
}
|
162
|
+
|
121
163
|
}
|
@@ -29,6 +29,7 @@ import arjdbc.jdbc.RubyJdbcConnection;
|
|
29
29
|
import arjdbc.jdbc.Callable;
|
30
30
|
|
31
31
|
import java.lang.reflect.Field;
|
32
|
+
import java.lang.reflect.InvocationTargetException;
|
32
33
|
import java.lang.reflect.Proxy;
|
33
34
|
import java.sql.Connection;
|
34
35
|
import java.sql.PreparedStatement;
|
@@ -234,10 +235,48 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
|
|
234
235
|
@Override
|
235
236
|
protected Connection newConnection() throws RaiseException, SQLException {
|
236
237
|
final Connection connection = super.newConnection();
|
238
|
+
if ( doStopCleanupThread() ) shutdownCleanupThread();
|
237
239
|
if ( doKillCancelTimer(connection) ) killCancelTimer(connection);
|
238
240
|
return connection;
|
239
241
|
}
|
240
242
|
|
243
|
+
private static Boolean stopCleanupThread;
|
244
|
+
static {
|
245
|
+
final String stopThread = System.getProperty("arjdbc.mysql.stop_cleanup_thread");
|
246
|
+
if ( stopThread != null ) stopCleanupThread = Boolean.parseBoolean(stopThread);
|
247
|
+
}
|
248
|
+
|
249
|
+
private static boolean doStopCleanupThread() throws SQLException {
|
250
|
+
// TODO when refactoring default behavior to "stop" consider not doing so for JNDI
|
251
|
+
return stopCleanupThread != null && stopCleanupThread.booleanValue();
|
252
|
+
}
|
253
|
+
|
254
|
+
private static boolean cleanupThreadShutdown;
|
255
|
+
|
256
|
+
private static void shutdownCleanupThread() {
|
257
|
+
if ( cleanupThreadShutdown ) return;
|
258
|
+
try {
|
259
|
+
Class threadClass = Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread");
|
260
|
+
threadClass.getMethod("shutdown").invoke(null);
|
261
|
+
}
|
262
|
+
catch (ClassNotFoundException e) {
|
263
|
+
debugMessage("INFO: missing MySQL JDBC cleanup thread: " + e);
|
264
|
+
}
|
265
|
+
catch (NoSuchMethodException e) {
|
266
|
+
debugMessage( e.toString() );
|
267
|
+
}
|
268
|
+
catch (IllegalAccessException e) {
|
269
|
+
debugMessage( e.toString() );
|
270
|
+
}
|
271
|
+
catch (InvocationTargetException e) {
|
272
|
+
debugMessage( e.getTargetException().toString() );
|
273
|
+
}
|
274
|
+
catch (SecurityException e) {
|
275
|
+
debugMessage( e.toString() );
|
276
|
+
}
|
277
|
+
finally { cleanupThreadShutdown = true; }
|
278
|
+
}
|
279
|
+
|
241
280
|
private static Boolean killCancelTimer;
|
242
281
|
static {
|
243
282
|
final String killTimer = System.getProperty("arjdbc.mysql.kill_cancel_timer");
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-jdbc-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sieger, Ola Bini, Karol Bucek and JRuby contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -238,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
238
238
|
version: '0'
|
239
239
|
requirements: []
|
240
240
|
rubyforge_project: jruby-extras
|
241
|
-
rubygems_version: 2.1.
|
241
|
+
rubygems_version: 2.1.11
|
242
242
|
signing_key:
|
243
243
|
specification_version: 4
|
244
244
|
summary: JDBC adapter for ActiveRecord, for use within JRuby on Rails.
|