activerecord-jdbc-adapter 50.8-java → 51.1-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/.gitignore +1 -2
- data/.travis.yml +26 -51
- data/Gemfile +3 -1
- data/README.md +9 -11
- data/Rakefile +15 -78
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/arjdbc/abstract/core.rb +4 -12
- data/lib/arjdbc/abstract/database_statements.rb +4 -10
- data/lib/arjdbc/abstract/statement_cache.rb +4 -4
- data/lib/arjdbc/abstract/transaction_support.rb +2 -9
- data/lib/arjdbc/jdbc.rb +4 -0
- data/lib/arjdbc/jdbc/column.rb +11 -5
- data/lib/arjdbc/jdbc/connection_methods.rb +9 -2
- data/lib/arjdbc/jdbc/error.rb +1 -1
- data/lib/arjdbc/jdbc/jdbc.rake +4 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +804 -0
- data/lib/arjdbc/mssql/column.rb +200 -0
- data/lib/arjdbc/mssql/connection_methods.rb +79 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql/adapter.rb +14 -11
- data/lib/arjdbc/mysql/connection_methods.rb +9 -18
- data/lib/arjdbc/postgresql/adapter.rb +108 -59
- data/lib/arjdbc/postgresql/column.rb +3 -6
- data/lib/arjdbc/postgresql/connection_methods.rb +3 -12
- data/lib/arjdbc/postgresql/oid_types.rb +14 -93
- data/lib/arjdbc/sqlite3/adapter.rb +171 -140
- data/lib/arjdbc/sqlite3/connection_methods.rb +1 -2
- data/lib/arjdbc/tasks/database_tasks.rb +36 -16
- data/lib/arjdbc/tasks/databases.rake +75 -32
- data/lib/arjdbc/tasks/databases3.rake +215 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/02-test.rake +3 -0
- data/rakelib/compile.rake +70 -0
- data/rakelib/db.rake +7 -21
- data/rakelib/rails.rake +4 -5
- data/src/java/arjdbc/ArJdbcModule.java +15 -5
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +87 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +1 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +41 -120
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +14 -310
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/postgresql/ByteaUtils.java +1 -0
- data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +13 -21
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +46 -41
- data/src/java/arjdbc/util/DateTimeUtils.java +5 -141
- data/src/java/arjdbc/util/QuotingUtils.java +7 -6
- data/src/java/arjdbc/util/StringHelper.java +20 -6
- metadata +25 -16
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +0 -61
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +0 -52
data/lib/arjdbc/version.rb
CHANGED
data/rakelib/01-tomcat.rake
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests)
|
2
2
|
|
3
|
-
TOMCAT_MAVEN_REPO = '
|
3
|
+
TOMCAT_MAVEN_REPO = 'http://repo2.maven.org/maven2/org/apache/tomcat'
|
4
4
|
TOMCAT_VERSION = '7.0.54'
|
5
5
|
|
6
6
|
DOWNLOAD_DIR = File.expand_path('../test/jars', File.dirname(__FILE__))
|
@@ -48,4 +48,4 @@ namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests)
|
|
48
48
|
rm jar_path if File.exist?(jar_path)
|
49
49
|
end
|
50
50
|
|
51
|
-
end
|
51
|
+
end
|
data/rakelib/02-test.rake
CHANGED
@@ -58,6 +58,7 @@ end
|
|
58
58
|
test_task_for :Derby, :desc => 'Run tests against (embedded) DerbyDB'
|
59
59
|
test_task_for :H2, :desc => 'Run tests against H2 database engine'
|
60
60
|
test_task_for :HSQLDB, :desc => 'Run tests against HyperSQL (Java) database'
|
61
|
+
test_task_for :MSSQL, :driver => :jtds, :database_name => 'MS-SQL (SQLServer)'
|
61
62
|
test_task_for :MySQL #, :prereqs => 'db:mysql'
|
62
63
|
task :test_mysql2 => :test_mysql
|
63
64
|
test_task_for :PostgreSQL, :driver => ENV['JDBC_POSTGRES_VERSION'] || 'postgres' #, :prereqs => 'db:postgresql'
|
@@ -75,6 +76,8 @@ end
|
|
75
76
|
test_task_for adapter, :desc => "Run tests against #{adapter} (ensure driver is on class-path)"
|
76
77
|
end
|
77
78
|
|
79
|
+
#test_task_for :MSSQL, :name => 'test_sqlserver', :driver => nil, :database_name => 'MS-SQL using SQLJDBC'
|
80
|
+
|
78
81
|
test_task_for :AS400, :desc => "Run tests against AS400 (DB2) (ensure driver is on class-path)",
|
79
82
|
:files => FileList["test/db2*_test.rb"] + FileList["test/db/db2/*_test.rb"]
|
80
83
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
jar_file = File.join(*%w(lib arjdbc jdbc adapter_java.jar))
|
2
|
+
begin
|
3
|
+
require 'ant'
|
4
|
+
directory classes = "pkg/classes"
|
5
|
+
CLEAN << classes
|
6
|
+
|
7
|
+
driver_jars = []
|
8
|
+
# PostgreSQL driver :
|
9
|
+
driver_jars << Dir.glob("jdbc-postgres/lib/*.jar").sort.last
|
10
|
+
|
11
|
+
file jar_file => FileList['src/java/**/*.java', 'pkg/classes'] do
|
12
|
+
rm_rf FileList["#{classes}/**/*"]
|
13
|
+
ant.javac :srcdir => "src/java", :destdir => "pkg/classes",
|
14
|
+
:source => "7", :target => "7", :debug => true, :deprecation => true,
|
15
|
+
:classpath => "${java.class.path}:${sun.boot.class.path}:#{driver_jars.join(':')}",
|
16
|
+
:includeantRuntime => false
|
17
|
+
|
18
|
+
ant.tstamp do |ts|
|
19
|
+
ts.format(:property => 'TODAY', :pattern => 'yyyy-MM-dd HH:mm:ss')
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'arjdbc/version'
|
24
|
+
rescue LoadError
|
25
|
+
path = File.expand_path('../lib', File.dirname(__FILE__))
|
26
|
+
unless $LOAD_PATH.include?(path)
|
27
|
+
$LOAD_PATH << path; retry
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
gem_version = Gem::Version.create(ArJdbc::VERSION)
|
32
|
+
if gem_version.segments.last == 'DEV'
|
33
|
+
version = gem_version.segments[0...-1] # 1.3.0.DEV -> 1.3.0
|
34
|
+
else
|
35
|
+
version = gem_version.segments.dup
|
36
|
+
end
|
37
|
+
version = version.join('.')
|
38
|
+
|
39
|
+
ant.manifest :file => 'MANIFEST.MF' do |mf|
|
40
|
+
mf.attribute :name => 'Built-By', :value => '${user.name}'
|
41
|
+
mf.attribute :name => 'Built-Time', :value => '${TODAY}'
|
42
|
+
mf.attribute :name => 'Built-Jdk', :value => '${java.version}'
|
43
|
+
mf.attribute :name => 'Built-JRuby', :value => JRUBY_VERSION
|
44
|
+
|
45
|
+
mf.attribute :name => 'Specification-Title', :value => 'ActiveRecord-JDBC'
|
46
|
+
mf.attribute :name => 'Specification-Version', :value => '1.3'
|
47
|
+
mf.attribute :name => 'Specification-Vendor', :value => 'JRuby'
|
48
|
+
mf.attribute :name => 'Implementation-Version', :value => version
|
49
|
+
mf.attribute :name => 'Implementation-Vendor', :value => 'The JRuby Team'
|
50
|
+
end
|
51
|
+
|
52
|
+
ant.jar :basedir => "pkg/classes", :includes => "**/*.class",
|
53
|
+
:destfile => jar_file, :manifest => 'MANIFEST.MF'
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Compile the native Java code."
|
57
|
+
task :jar => jar_file
|
58
|
+
|
59
|
+
namespace :jar do
|
60
|
+
task :force do
|
61
|
+
rm jar_file if File.exist?(jar_file)
|
62
|
+
Rake::Task['jar'].invoke
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
rescue LoadError
|
67
|
+
task :jar do
|
68
|
+
puts "Run 'jar' with JRuby to re-compile the agent extension class"
|
69
|
+
end
|
70
|
+
end
|
data/rakelib/db.rake
CHANGED
@@ -4,35 +4,25 @@ namespace :db do
|
|
4
4
|
task :mysql do
|
5
5
|
require File.expand_path('../../test/shared_helper', __FILE__)
|
6
6
|
fail "could not create test database: mysql executable not found" unless mysql = which('mysql')
|
7
|
-
|
8
7
|
load 'test/db/mysql_config.rb' # rescue nil
|
9
8
|
enc = MYSQL_CONFIG[:encoding] || 'utf8' # 'utf8mb4'
|
10
9
|
puts MYSQL_CONFIG.inspect if $VERBOSE
|
11
|
-
|
12
|
-
clean_script = sql_script <<-SQL, 'mysqlclean'
|
13
|
-
DROP USER #{MYSQL_CONFIG[:username]}@localhost;
|
14
|
-
SQL
|
15
|
-
|
10
|
+
# DROP USER arjdbc@localhost; __ERROR 1396 (HY000): Operation CREATE USER failed__
|
16
11
|
script = sql_script <<-SQL, 'mysql'
|
17
12
|
DROP DATABASE IF EXISTS `#{MYSQL_CONFIG[:database]}`;
|
18
13
|
CREATE DATABASE `#{MYSQL_CONFIG[:database]}` DEFAULT CHARACTER SET `#{enc}` COLLATE `#{enc}_general_ci`;
|
19
|
-
CREATE USER #{MYSQL_CONFIG[:username]}@localhost IDENTIFIED BY '#{MYSQL_CONFIG[:password]}';
|
20
14
|
GRANT ALL PRIVILEGES ON `#{MYSQL_CONFIG[:database]}`.* TO #{MYSQL_CONFIG[:username]}@localhost;
|
21
15
|
GRANT ALL PRIVILEGES ON `test\_%`.* TO #{MYSQL_CONFIG[:username]}@localhost;
|
16
|
+
SET PASSWORD FOR #{MYSQL_CONFIG[:username]}@localhost = PASSWORD('#{MYSQL_CONFIG[:password]}');
|
22
17
|
SQL
|
23
|
-
|
24
18
|
params = { '-u' => 'root' }
|
25
|
-
if ENV['DATABASE_YML']
|
26
|
-
|
27
|
-
params['-p'] = YAML.load(File.new(ENV['DATABASE_YML']))["production"]["password"]
|
19
|
+
if ENV['DATABASE_YML']; require 'yaml'
|
20
|
+
params['-p'] = YAML.load(File.new(ENV['DATABASE_YML']))["production"]["password"]
|
28
21
|
end
|
29
22
|
params['-u'] = ENV['MY_USER'] if ENV['MY_USER']
|
30
23
|
params['-p'] = ENV['MY_PASSWORD'] if ENV['MY_PASSWORD']
|
31
|
-
|
32
24
|
puts "Creating MySQL (test) database: #{MYSQL_CONFIG[:database]}"
|
33
|
-
|
34
|
-
sh "cat #{clean_script.path} | #{mysql_cmd}", verbose: false
|
35
|
-
sh "cat #{script.path} | #{mysql_cmd}", verbose: $VERBOSE # so password is not echoed
|
25
|
+
sh "cat #{script.path} | #{mysql} -f #{params.map {|k, v| "#{k}#{v}"}.join(' ')}", :verbose => $VERBOSE # so password is not echoed
|
36
26
|
end
|
37
27
|
|
38
28
|
desc "Creates the test database for PostgreSQL"
|
@@ -40,10 +30,8 @@ GRANT ALL PRIVILEGES ON `test\_%`.* TO #{MYSQL_CONFIG[:username]}@localhost;
|
|
40
30
|
require File.expand_path('../../test/shared_helper', __FILE__)
|
41
31
|
fail 'could not create test database: psql executable not found' unless psql = which('psql')
|
42
32
|
fail 'could not create test database: missing "postgres" role' unless PostgresHelper.postgres_role?
|
43
|
-
|
44
33
|
load 'test/db/postgres_config.rb' # rescue nil
|
45
34
|
puts POSTGRES_CONFIG.inspect if $VERBOSE
|
46
|
-
|
47
35
|
script = sql_script <<-SQL, 'psql'
|
48
36
|
DROP DATABASE IF EXISTS #{POSTGRES_CONFIG[:database]};
|
49
37
|
DROP USER IF EXISTS #{POSTGRES_CONFIG[:username]};
|
@@ -52,14 +40,12 @@ CREATE DATABASE #{POSTGRES_CONFIG[:database]} OWNER #{POSTGRES_CONFIG[:username]
|
|
52
40
|
TEMPLATE template0
|
53
41
|
ENCODING '#{POSTGRES_CONFIG[:encoding]}' LC_COLLATE '#{POSTGRES_CONFIG[:collate]}' LC_CTYPE '#{POSTGRES_CONFIG[:collate]}';
|
54
42
|
SQL
|
55
|
-
|
56
43
|
params = { '-U' => ENV['PSQL_USER'] || 'postgres' }
|
57
44
|
params['-q'] = nil unless $VERBOSE
|
58
|
-
|
59
45
|
puts "Creating PostgreSQL (test) database: #{POSTGRES_CONFIG[:database]}"
|
60
|
-
sh "cat #{script.path} | #{psql} #{params.to_a.join(' ')}", verbose
|
46
|
+
sh "cat #{script.path} | #{psql} #{params.to_a.join(' ')}", :verbose => $VERBOSE
|
61
47
|
end
|
62
|
-
task postgres
|
48
|
+
task :postgres => :postgresql
|
63
49
|
|
64
50
|
private
|
65
51
|
|
data/rakelib/rails.rake
CHANGED
@@ -9,15 +9,14 @@ namespace :rails do
|
|
9
9
|
if ENV['RAILS']
|
10
10
|
ar_path = File.join(ENV['RAILS'], 'activerecord')
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
unless ar_path && File.exist?(ar_path)
|
14
|
-
ar_path = `bundle info --path activerecord`.
|
14
|
+
ar_path = `bundle info --path activerecord`.chomp
|
15
15
|
end
|
16
16
|
|
17
17
|
unless File.exist? ar_test_dir = File.join(ar_path, 'test')
|
18
18
|
raise "can not directly load Rails tests;" +
|
19
|
-
" try setting a local repository path e.g. export RAILS=`pwd`/../rails
|
20
|
-
" failed guess: #{ar_path}"
|
19
|
+
" try setting a local repository path e.g. export RAILS=`pwd`/../rails"
|
21
20
|
end
|
22
21
|
|
23
22
|
driver = "jdbc-#{ENV['DRIVER'] ? ENV['DRIVER'].downcase : (adapter =~ /postgres/i ? 'postgres' : adapter)}"
|
@@ -51,7 +50,7 @@ namespace :rails do
|
|
51
50
|
ruby_opts_string += " -C \"#{ar_path}\""
|
52
51
|
ruby_opts_string += " -rbundler/setup"
|
53
52
|
ruby_opts_string += " -rminitest -rminitest/excludes" unless ENV['NO_EXCLUDES'].eql?('true')
|
54
|
-
file_list = ENV["TEST"] ? FileList[ ENV["TEST"]
|
53
|
+
file_list = ENV["TEST"] ? FileList[ ENV["TEST"] ] : test_files_finder.call
|
55
54
|
file_list_string = file_list.map { |fn| "\"#{fn}\"" }.join(' ')
|
56
55
|
# test_loader_code = "-e \"ARGV.each{|f| require f}\"" # :direct
|
57
56
|
option_list = ( ENV["TESTOPTS"] || ENV["TESTOPT"] || ENV["TEST_OPTS"] || '' )
|
@@ -29,6 +29,7 @@ import java.util.HashMap;
|
|
29
29
|
import java.util.Map;
|
30
30
|
import java.util.WeakHashMap;
|
31
31
|
|
32
|
+
import org.jruby.NativeException;
|
32
33
|
import org.jruby.Ruby;
|
33
34
|
import org.jruby.RubyArray;
|
34
35
|
import org.jruby.RubyClass;
|
@@ -147,7 +148,13 @@ public class ArJdbcModule {
|
|
147
148
|
}
|
148
149
|
}
|
149
150
|
catch (ClassNotFoundException e) { /* ignored */ }
|
150
|
-
catch (NoSuchMethodException
|
151
|
+
catch (NoSuchMethodException e) {
|
152
|
+
throw newNativeException(runtime, e);
|
153
|
+
}
|
154
|
+
catch (IllegalAccessException e) {
|
155
|
+
throw newNativeException(runtime, e);
|
156
|
+
}
|
157
|
+
catch (InvocationTargetException e) {
|
151
158
|
throw newNativeException(runtime, e);
|
152
159
|
}
|
153
160
|
|
@@ -256,15 +263,18 @@ public class ArJdbcModule {
|
|
256
263
|
try {
|
257
264
|
return klass.getMethod(name, argType).invoke(null, arg);
|
258
265
|
}
|
259
|
-
catch (IllegalAccessException
|
266
|
+
catch (IllegalAccessException e) {
|
267
|
+
throw newNativeException(runtime, e);
|
268
|
+
}
|
269
|
+
catch (InvocationTargetException e) {
|
260
270
|
throw newNativeException(runtime, e);
|
261
271
|
}
|
262
272
|
}
|
263
273
|
|
264
274
|
private static RaiseException newNativeException(final Ruby runtime, final Throwable cause) {
|
265
|
-
|
266
|
-
|
267
|
-
return
|
275
|
+
RubyClass nativeClass = runtime.getClass(NativeException.CLASS_NAME);
|
276
|
+
NativeException nativeException = new NativeException(runtime, nativeClass, cause);
|
277
|
+
return new RaiseException(cause, nativeException);
|
268
278
|
}
|
269
279
|
|
270
280
|
@JRubyMethod(meta = true)
|
@@ -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 context.runtime
|
87
|
-
"adapter requires Derby >= 10.5 got: " + major + "." + minor + "");
|
86
|
+
throw new RaiseException(context.runtime, errorClass,
|
87
|
+
"adapter requires Derby >= 10.5 got: " + major + "." + minor + "", false);
|
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,6 +27,12 @@ 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;
|
30
36
|
|
31
37
|
/**
|
32
38
|
* Interface to be implemented in Ruby for retrieving a new connection.
|
@@ -43,3 +49,84 @@ public interface ConnectionFactory {
|
|
43
49
|
Connection newConnection() throws SQLException;
|
44
50
|
|
45
51
|
}
|
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,6 +68,7 @@ final class DataSourceConnectionFactory implements ConnectionFactory {
|
|
68
68
|
|
69
69
|
@Override
|
70
70
|
public Connection newConnection() throws SQLException {
|
71
|
+
DataSource dataSource = this.dataSource;
|
71
72
|
// in case DS failed previously look it up again from JNDI :
|
72
73
|
if (dataSource == null) {
|
73
74
|
lookupDataSource();
|
@@ -128,7 +128,6 @@ 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
|
132
131
|
|
133
132
|
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
134
133
|
super(runtime, metaClass);
|
@@ -285,7 +284,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
285
284
|
public static int mapTransactionIsolationLevel(final IRubyObject isolation) {
|
286
285
|
final Object isolationString;
|
287
286
|
if ( isolation instanceof RubySymbol ) {
|
288
|
-
isolationString =
|
287
|
+
isolationString = isolation.toString(); // RubySymbol.toString (interned)
|
289
288
|
}
|
290
289
|
else {
|
291
290
|
isolationString = isolation.asString().toString().toLowerCase(Locale.ENGLISH).intern();
|
@@ -487,7 +486,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
487
486
|
savepoint = ((IRubyObject) savepoint).toJava(Savepoint.class);
|
488
487
|
}
|
489
488
|
|
490
|
-
releaseSavepoint(
|
489
|
+
connection.releaseSavepoint((Savepoint) savepoint);
|
491
490
|
return context.nil;
|
492
491
|
}
|
493
492
|
catch (SQLException e) {
|
@@ -495,11 +494,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
495
494
|
}
|
496
495
|
}
|
497
496
|
|
498
|
-
// MSSQL doesn't support releasing savepoints so we make it possible to override the actual release action
|
499
|
-
protected void releaseSavepoint(final Connection connection, final Savepoint savepoint) throws SQLException {
|
500
|
-
connection.releaseSavepoint(savepoint);
|
501
|
-
}
|
502
|
-
|
503
497
|
protected static RuntimeException newSavepointNotSetError(final ThreadContext context, final IRubyObject name, final String op) {
|
504
498
|
RubyClass StatementInvalid = ActiveRecord(context).getClass("StatementInvalid");
|
505
499
|
return context.runtime.newRaiseException(StatementInvalid, "could not " + op + " savepoint: '" + name + "' (not set)");
|
@@ -577,11 +571,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
577
571
|
else {
|
578
572
|
this.configureConnection = value != context.runtime.getFalse();
|
579
573
|
}
|
580
|
-
|
581
|
-
IRubyObject jdbcFetchSize = getConfigValue(context, "jdbc_fetch_size");
|
582
|
-
if (jdbcFetchSize != context.nil) {
|
583
|
-
this.fetchSize = RubyNumeric.fix2int(jdbcFetchSize);
|
584
|
-
}
|
585
574
|
}
|
586
575
|
|
587
576
|
@JRubyMethod(name = "adapter")
|
@@ -727,10 +716,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
727
716
|
|
728
717
|
private void connectImpl(final boolean forceConnection) throws SQLException {
|
729
718
|
setConnection( forceConnection ? newConnection() : null );
|
730
|
-
if (forceConnection)
|
731
|
-
if (getConnectionImpl() == null) throw new SQLException("Didn't get a connection. Wrong URL?");
|
732
|
-
configureConnection();
|
733
|
-
}
|
719
|
+
if ( forceConnection ) configureConnection();
|
734
720
|
}
|
735
721
|
|
736
722
|
@JRubyMethod(name = "read_only?")
|
@@ -832,7 +818,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
832
818
|
// is called, so we have to process the result sets as we get them
|
833
819
|
// this shouldn't be an issue in most cases since we're only getting 1 result set anyways
|
834
820
|
result = mapExecuteResult(context, connection, resultSet);
|
835
|
-
resultSet.close();
|
836
821
|
} else {
|
837
822
|
result = context.runtime.newFixnum(updateCount);
|
838
823
|
}
|
@@ -866,7 +851,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
866
851
|
else {
|
867
852
|
statement.setEscapeProcessing(escapeProcessing.isTrue());
|
868
853
|
}
|
869
|
-
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
870
854
|
return statement;
|
871
855
|
}
|
872
856
|
|
@@ -888,31 +872,15 @@ public class RubyJdbcConnection extends RubyObject {
|
|
888
872
|
return mapQueryResult(context, connection, resultSet);
|
889
873
|
}
|
890
874
|
|
891
|
-
private static String[] createStatementPk(IRubyObject pk) {
|
892
|
-
String[] statementPk;
|
893
|
-
if (pk instanceof RubyArray) {
|
894
|
-
RubyArray ary = (RubyArray) pk;
|
895
|
-
int size = ary.size();
|
896
|
-
statementPk = new String[size];
|
897
|
-
for (int i = 0; i < size; i++) {
|
898
|
-
statementPk[i] = sqlString(ary.eltInternal(i));
|
899
|
-
}
|
900
|
-
} else {
|
901
|
-
statementPk = new String[] { sqlString(pk) };
|
902
|
-
}
|
903
|
-
return statementPk;
|
904
|
-
}
|
905
|
-
|
906
875
|
/**
|
907
876
|
* Executes an INSERT SQL statement
|
908
877
|
* @param context
|
909
878
|
* @param sql
|
910
|
-
* @param pk Rails PK
|
911
879
|
* @return ActiveRecord::Result
|
912
880
|
* @throws SQLException
|
913
881
|
*/
|
914
|
-
@JRubyMethod(name = "
|
915
|
-
public IRubyObject
|
882
|
+
@JRubyMethod(name = "execute_insert", required = 1)
|
883
|
+
public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
|
916
884
|
return withConnection(context, new Callable<IRubyObject>() {
|
917
885
|
public IRubyObject call(final Connection connection) throws SQLException {
|
918
886
|
Statement statement = null;
|
@@ -920,13 +888,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
920
888
|
try {
|
921
889
|
|
922
890
|
statement = createStatement(context, connection);
|
923
|
-
|
924
|
-
if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
|
925
|
-
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
|
926
|
-
} else {
|
927
|
-
statement.executeUpdate(query, createStatementPk(pk));
|
928
|
-
}
|
929
|
-
|
891
|
+
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
|
930
892
|
return mapGeneratedKeys(context, connection, statement);
|
931
893
|
|
932
894
|
} catch (final SQLException e) {
|
@@ -939,35 +901,23 @@ public class RubyJdbcConnection extends RubyObject {
|
|
939
901
|
});
|
940
902
|
}
|
941
903
|
|
942
|
-
@Deprecated
|
943
|
-
@JRubyMethod(name = "execute_insert", required = 1)
|
944
|
-
public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
|
945
|
-
return execute_insert_pk(context, sql, context.nil);
|
946
|
-
}
|
947
|
-
|
948
904
|
/**
|
949
905
|
* Executes an INSERT SQL statement using a prepared statement
|
950
906
|
* @param context
|
951
907
|
* @param sql
|
952
908
|
* @param binds RubyArray of values to be bound to the query
|
953
|
-
* @param pk Rails PK
|
954
909
|
* @return ActiveRecord::Result
|
955
910
|
* @throws SQLException
|
956
911
|
*/
|
957
|
-
@JRubyMethod(name = "
|
958
|
-
public IRubyObject
|
959
|
-
final IRubyObject pk) {
|
912
|
+
@JRubyMethod(name = "execute_insert", required = 2)
|
913
|
+
public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql, final IRubyObject binds) {
|
960
914
|
return withConnection(context, new Callable<IRubyObject>() {
|
961
915
|
public IRubyObject call(final Connection connection) throws SQLException {
|
962
916
|
PreparedStatement statement = null;
|
963
917
|
final String query = sqlString(sql);
|
964
918
|
try {
|
965
|
-
if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
|
966
|
-
statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
|
967
|
-
} else {
|
968
|
-
statement = connection.prepareStatement(query, createStatementPk(pk));
|
969
|
-
}
|
970
919
|
|
920
|
+
statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
|
971
921
|
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
972
922
|
statement.executeUpdate();
|
973
923
|
return mapGeneratedKeys(context, connection, statement);
|
@@ -982,12 +932,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
982
932
|
});
|
983
933
|
}
|
984
934
|
|
985
|
-
@Deprecated
|
986
|
-
@JRubyMethod(name = "execute_insert", required = 2)
|
987
|
-
public IRubyObject execute_insert(final ThreadContext context, final IRubyObject binds, final IRubyObject sql) {
|
988
|
-
return execute_insert_pk(context, sql, binds, context.nil);
|
989
|
-
}
|
990
|
-
|
991
935
|
/**
|
992
936
|
* Executes an UPDATE (DELETE) SQL statement
|
993
937
|
* @param context
|
@@ -1101,7 +1045,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1101
1045
|
else {
|
1102
1046
|
final PreparedStatement prepStatement;
|
1103
1047
|
statement = prepStatement = connection.prepareStatement(query);
|
1104
|
-
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1105
1048
|
statement.setMaxRows(maxRows); // zero means there is no limit
|
1106
1049
|
setStatementParameters(context, connection, prepStatement, binds);
|
1107
1050
|
hasResult = prepStatement.execute();
|
@@ -1169,24 +1112,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1169
1112
|
});
|
1170
1113
|
}
|
1171
1114
|
|
1172
|
-
/**
|
1173
|
-
* Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
|
1174
|
-
* @param context which context this method is executing on.
|
1175
|
-
* @param sql the query to prepare-
|
1176
|
-
* @return a Ruby <code>PreparedStatement</code>
|
1177
|
-
*/
|
1178
|
-
@JRubyMethod(required = 1)
|
1179
|
-
public IRubyObject prepare_statement(final ThreadContext context, final IRubyObject sql) {
|
1180
|
-
return withConnection(context, new Callable<IRubyObject>() {
|
1181
|
-
public IRubyObject call(Connection connection) throws SQLException {
|
1182
|
-
final String query = sql.convertToString().getUnicodeValue();
|
1183
|
-
PreparedStatement statement = connection.prepareStatement(query);
|
1184
|
-
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1185
|
-
return JavaUtil.convertJavaToRuby(context.runtime, statement);
|
1186
|
-
}
|
1187
|
-
});
|
1188
|
-
}
|
1189
|
-
|
1190
1115
|
// Called from exec_query in abstract/database_statements
|
1191
1116
|
/**
|
1192
1117
|
* Executes a query and returns the (AR) result. There are three parameters:
|
@@ -1217,7 +1142,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1217
1142
|
statement = (PreparedStatement) JavaEmbedUtils.rubyToJava(cachedStatement);
|
1218
1143
|
} else {
|
1219
1144
|
statement = connection.prepareStatement(query);
|
1220
|
-
if (fetchSize != 0) statement.setFetchSize(fetchSize);
|
1221
1145
|
}
|
1222
1146
|
|
1223
1147
|
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
@@ -1225,7 +1149,12 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1225
1149
|
if (statement.execute()) {
|
1226
1150
|
ResultSet resultSet = statement.getResultSet();
|
1227
1151
|
IRubyObject results = mapQueryResult(context, connection, resultSet);
|
1228
|
-
|
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
|
+
}
|
1229
1158
|
|
1230
1159
|
return results;
|
1231
1160
|
} else {
|
@@ -1878,7 +1807,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1878
1807
|
if ( url.isNil() || ( driver.isNil() && driver_instance.isNil() ) ) {
|
1879
1808
|
final Ruby runtime = context.runtime;
|
1880
1809
|
final RubyClass errorClass = getConnectionNotEstablished( runtime );
|
1881
|
-
throw runtime
|
1810
|
+
throw new RaiseException(runtime, errorClass, "adapter requires :driver class and jdbc :url", false);
|
1882
1811
|
}
|
1883
1812
|
|
1884
1813
|
final String jdbcURL = buildURL(context, url);
|
@@ -1895,7 +1824,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1895
1824
|
return factory;
|
1896
1825
|
}
|
1897
1826
|
else {
|
1898
|
-
setConnectionFactory(factory = new
|
1827
|
+
setConnectionFactory(factory = new RubyConnectionFactoryImpl(
|
1899
1828
|
driver_instance, context.runtime.newString(jdbcURL),
|
1900
1829
|
( username.isNil() ? username : username.asString() ),
|
1901
1830
|
( password.isNil() ? password : password.asString() )
|
@@ -2556,8 +2485,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2556
2485
|
while ( arrayResult.next() ) {
|
2557
2486
|
array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
|
2558
2487
|
}
|
2559
|
-
arrayResult.close();
|
2560
|
-
|
2561
2488
|
return array;
|
2562
2489
|
}
|
2563
2490
|
finally { if ( value != null ) value.free(); }
|
@@ -2740,23 +2667,27 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2740
2667
|
|
2741
2668
|
final IRubyObject value = value_site.call(context, attribute, attribute);
|
2742
2669
|
|
2743
|
-
if (
|
2670
|
+
if (value instanceof RubyInteger) {
|
2744
2671
|
return "integer";
|
2745
2672
|
}
|
2746
2673
|
|
2747
|
-
if (
|
2674
|
+
if (value instanceof RubyNumeric) {
|
2748
2675
|
return "float";
|
2749
2676
|
}
|
2750
2677
|
|
2751
|
-
if (
|
2678
|
+
if (value instanceof RubyTime) {
|
2752
2679
|
return "timestamp";
|
2753
2680
|
}
|
2754
2681
|
|
2682
|
+
if (value instanceof RubyBoolean) {
|
2683
|
+
return "boolean";
|
2684
|
+
}
|
2685
|
+
|
2755
2686
|
return "string";
|
2756
2687
|
}
|
2757
2688
|
|
2758
2689
|
protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
|
2759
|
-
return timeInDefaultTimeZone(context,
|
2690
|
+
return timeInDefaultTimeZone(context, toTime(context, value));
|
2760
2691
|
}
|
2761
2692
|
|
2762
2693
|
protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final RubyTime time) {
|
@@ -2768,14 +2699,11 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2768
2699
|
return timeInDefaultTZ;
|
2769
2700
|
}
|
2770
2701
|
|
2771
|
-
protected final DateTime dateTimeInDefaultTimeZone(final ThreadContext context, final DateTime dateTime) {
|
2772
|
-
final DateTimeZone defaultZone = getDefaultTimeZone(context);
|
2773
|
-
if (defaultZone == dateTime.getZone()) return dateTime;
|
2774
|
-
return dateTime.withZone(defaultZone);
|
2775
|
-
}
|
2776
|
-
|
2777
2702
|
public static RubyTime toTime(final ThreadContext context, final IRubyObject value) {
|
2778
|
-
|
2703
|
+
if ( ! ( value instanceof RubyTime ) ) { // unlikely
|
2704
|
+
return (RubyTime) TypeConverter.convertToTypeWithCheck(value, context.runtime.getTime(), "to_time");
|
2705
|
+
}
|
2706
|
+
return (RubyTime) value;
|
2779
2707
|
}
|
2780
2708
|
|
2781
2709
|
protected boolean isDefaultTimeZoneUTC(final ThreadContext context) {
|
@@ -2788,7 +2716,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2788
2716
|
|
2789
2717
|
private String default_timezone(final ThreadContext context) {
|
2790
2718
|
final RubyClass base = getBase(context.runtime);
|
2791
|
-
return default_timezone.call(context, base, base).
|
2719
|
+
return default_timezone.call(context, base, base).toString(); // :utc (or :local)
|
2792
2720
|
}
|
2793
2721
|
|
2794
2722
|
// ActiveRecord::Base.default_timezone
|
@@ -2875,8 +2803,8 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2875
2803
|
final int index, IRubyObject value,
|
2876
2804
|
final IRubyObject attribute, final int type) throws SQLException {
|
2877
2805
|
|
2878
|
-
final RubyTime timeValue =
|
2879
|
-
final DateTime dateTime =
|
2806
|
+
final RubyTime timeValue = timeInDefaultTimeZone(context, value);
|
2807
|
+
final DateTime dateTime = timeValue.getDateTime();
|
2880
2808
|
final Timestamp timestamp = new Timestamp(dateTime.getMillis());
|
2881
2809
|
// 1942-11-30T01:02:03.123_456
|
2882
2810
|
if (timeValue.getNSec() > 0) timestamp.setNanos((int) (timestamp.getNanos() + timeValue.getNSec()));
|
@@ -2970,13 +2898,14 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2970
2898
|
final RubySymbol type = (RubySymbol) attributeSQLType(context, attribute);
|
2971
2899
|
|
2972
2900
|
// For some reason the driver doesn't like "character varying" as a type
|
2973
|
-
if ( type.eql(context.runtime.newSymbol("string")) )
|
2901
|
+
if ( type.eql(context.runtime.newSymbol("string")) ){
|
2902
|
+
return "text";
|
2903
|
+
}
|
2974
2904
|
|
2975
2905
|
final RubyHash nativeTypes = (RubyHash) getAdapter().callMethod(context, "native_database_types");
|
2976
|
-
// e.g. `integer: { name: 'integer' }`
|
2977
2906
|
final RubyHash typeInfo = (RubyHash) nativeTypes.op_aref(context, type);
|
2978
2907
|
|
2979
|
-
return typeInfo.op_aref(context, context.runtime.newSymbol("name")).toString();
|
2908
|
+
return typeInfo.op_aref(context, context.runtime.newSymbol("name")).asString().toString();
|
2980
2909
|
}
|
2981
2910
|
|
2982
2911
|
protected void setXmlParameter(final ThreadContext context,
|
@@ -3077,7 +3006,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3077
3006
|
private void handleNotConnected() {
|
3078
3007
|
final Ruby runtime = getRuntime();
|
3079
3008
|
final RubyClass errorClass = getConnectionNotEstablished( runtime );
|
3080
|
-
throw runtime
|
3009
|
+
throw new RaiseException(runtime, errorClass, "no connection available", false);
|
3081
3010
|
}
|
3082
3011
|
|
3083
3012
|
/**
|
@@ -3517,14 +3446,12 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3517
3446
|
if ( ! gotConnection ) { // SQLException from driver/data-source
|
3518
3447
|
reconnectOnRetry = connected;
|
3519
3448
|
}
|
3520
|
-
else if (!autoCommit) {
|
3521
|
-
// never retry inside a transaction
|
3522
|
-
break;
|
3523
|
-
}
|
3524
3449
|
else if ( isTransient(exception) ) {
|
3525
3450
|
reconnectOnRetry = false; // continue;
|
3526
3451
|
}
|
3527
3452
|
else {
|
3453
|
+
if ( ! autoCommit ) break; // do not retry if (inside) transactions
|
3454
|
+
|
3528
3455
|
if ( isConnectionValid(context, getConnectionImpl()) ) {
|
3529
3456
|
break; // connection not broken yet failed (do not retry)
|
3530
3457
|
}
|
@@ -3614,7 +3541,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3614
3541
|
return (RaiseException) exception;
|
3615
3542
|
}
|
3616
3543
|
if ( exception instanceof RuntimeException ) {
|
3617
|
-
return
|
3544
|
+
return RaiseException.createNativeRaiseException(runtime, exception);
|
3618
3545
|
}
|
3619
3546
|
// NOTE: compat - maybe makes sense or maybe not (e.g. IOException) :
|
3620
3547
|
return wrapException(context, getJDBCError(runtime), exception);
|
@@ -3627,7 +3554,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3627
3554
|
|
3628
3555
|
public static RaiseException wrapException(final ThreadContext context,
|
3629
3556
|
final RubyClass errorClass, final Throwable exception, final String message) {
|
3630
|
-
final RaiseException error = context.runtime
|
3557
|
+
final RaiseException error = new RaiseException(context.runtime, errorClass, message, true);
|
3631
3558
|
error.initCause(exception);
|
3632
3559
|
return error;
|
3633
3560
|
}
|
@@ -3961,12 +3888,6 @@ public class RubyJdbcConnection extends RubyObject {
|
|
3961
3888
|
}
|
3962
3889
|
}
|
3963
3890
|
|
3964
|
-
public static void debugMessage(final ThreadContext context, final IRubyObject obj) {
|
3965
|
-
if ( isDebug(context.runtime) ) {
|
3966
|
-
debugMessage(context.runtime, obj.callMethod(context, "inspect"));
|
3967
|
-
}
|
3968
|
-
}
|
3969
|
-
|
3970
3891
|
public static void debugMessage(final Ruby runtime, final String msg, final Object e) {
|
3971
3892
|
if ( isDebug(runtime) ) {
|
3972
3893
|
final PrintStream out = runtime != null ? runtime.getOut() : System.out;
|