ruby-plsql 0.3.1 → 0.4.0
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.
- data/.gitignore +10 -0
- data/History.txt +7 -0
- data/README.rdoc +97 -29
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +85 -11
- data/lib/plsql/jdbc_connection.rb +317 -165
- data/lib/plsql/oci_connection.rb +158 -78
- data/lib/plsql/package.rb +5 -5
- data/lib/plsql/procedure.rb +69 -134
- data/lib/plsql/procedure_call.rb +345 -0
- data/lib/plsql/schema.rb +58 -40
- data/lib/plsql/sequence.rb +49 -0
- data/lib/plsql/sql_statements.rb +61 -0
- data/lib/plsql/table.rb +285 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +7 -40
- data/spec/plsql/connection_spec.rb +40 -24
- data/spec/plsql/procedure_spec.rb +1145 -453
- data/spec/plsql/schema_spec.rb +9 -2
- data/spec/plsql/sequence_spec.rb +67 -0
- data/spec/plsql/sql_statements_spec.rb +109 -0
- data/spec/plsql/table_spec.rb +269 -0
- data/spec/spec_helper.rb +20 -10
- metadata +35 -34
- data/lib/ruby_plsql/version.rb +0 -3
data/.gitignore
ADDED
data/History.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,58 +1,126 @@
|
|
1
1
|
= ruby-plsql
|
2
2
|
|
3
|
-
|
3
|
+
Ruby API for calling Oracle PL/SQL procedures.
|
4
4
|
|
5
|
-
== DESCRIPTION
|
5
|
+
== DESCRIPTION
|
6
6
|
|
7
|
-
ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures.
|
8
|
-
ruby-plsql support both Ruby 1.8 MRI, Ruby 1.9.1 YARV and JRuby runtime environments.
|
9
|
-
This gem requires ruby-oci8 library version 1.x (if MRI is used) or 2.x (if Ruby 1.9.1 is used) or Oracle JDBC driver (ojdbc14.jar) (if JRuby is used) for connection to Oracle database.
|
7
|
+
ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures. It could be used both for accessing Oracle PL/SQL API procedures in legacy applications as well as it could be used to create PL/SQL unit tests using Ruby testing libraries.
|
10
8
|
|
11
|
-
|
9
|
+
NUMBER, VARCHAR2, DATE, TIMESTAMP, CLOB, BLOB, BOOLEAN, PL/SQL RECORD, TABLE, VARRAY, OBJECT and CURSOR types are supported for input and output parameters and return values of PL/SQL procedures and functions.
|
12
10
|
|
13
|
-
|
11
|
+
ruby-plsql support both Ruby 1.8 MRI, Ruby 1.9.1 YARV and JRuby 1.3/1.4 runtime environments.
|
14
12
|
|
15
|
-
==
|
13
|
+
== USAGE
|
16
14
|
|
17
|
-
|
15
|
+
=== Calling PL/SQL functions and procedures:
|
18
16
|
|
19
|
-
|
17
|
+
require "rubygems"
|
18
|
+
require "ruby-plsql"
|
20
19
|
|
21
|
-
|
20
|
+
plsql.connection = OCI8.new("hr","hr","xe")
|
22
21
|
|
23
|
-
|
22
|
+
plsql.test_uppercase('xxx') # => "XXX"
|
23
|
+
plsql.test_uppercase(:p_string => 'xxx') # => "XXX"
|
24
|
+
plsql.test_copy("abc", nil, nil) # => { :p_to => "abc", :p_to_double => "abcabc" }
|
25
|
+
plsql.test_copy(:p_from => "abc", :p_to => nil, :p_to_double => nil)
|
26
|
+
# => { :p_to => "abc", :p_to_double => "abcabc" }
|
27
|
+
plsql.hr.test_uppercase('xxx') # => "XXX"
|
28
|
+
plsql.test_package.test_uppercase('xxx') # => 'XXX'
|
24
29
|
|
25
|
-
|
30
|
+
# PL/SQL records or object type parameters should be passed as Hash
|
31
|
+
p_employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31) }
|
32
|
+
plsql.test_full_name(p_employee)
|
26
33
|
|
27
|
-
|
28
|
-
plsql.
|
29
|
-
plsql.test_copy("abc", nil, nil) # => { :p_to => "abc", :p_to_double => "abcabc" }
|
30
|
-
plsql.test_copy(:p_from => "abc", :p_to => nil, :p_to_double => nil)
|
31
|
-
# => { :p_to => "abc", :p_to_double => "abcabc" }
|
32
|
-
plsql.hr.test_uppercase('xxx') # => "XXX"
|
33
|
-
plsql.test_package.test_uppercase('xxx') # => 'XXX'
|
34
|
+
# TABLE or VARRAY parameters should be passed as Array
|
35
|
+
plsql.test_sum([1,2,3,4])
|
34
36
|
|
35
|
-
|
37
|
+
# Nested objects or arrays are also supported
|
38
|
+
p_employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31),
|
39
|
+
:address => {:street => 'Street', :city => 'City', :country => 'Country},
|
40
|
+
:phones => [{:type => 'mobile', :phone_number => '123456'}, {:type => 'fixed', :phone_number => '654321'}]}
|
41
|
+
plsql.test_store_employee(p_employee)
|
36
42
|
|
43
|
+
# Returned cursor can be fetched
|
44
|
+
plsql.test_cursor do |cursor|
|
45
|
+
cursor.fetch # => one row from cursor
|
46
|
+
cursor.fetch_all # => all rows from cursor
|
47
|
+
end
|
48
|
+
|
49
|
+
plsql.connection.autocommit = false
|
50
|
+
plsql.commit
|
51
|
+
plsql.rollback
|
52
|
+
|
53
|
+
plsql.logoff
|
54
|
+
|
55
|
+
Look at RSpec tests under spec directory for more usage examples.
|
56
|
+
|
57
|
+
|
58
|
+
=== Table operations:
|
59
|
+
|
60
|
+
ruby-plsql also provides simple API for select/insert/update/delete table operations (with Sequel-like syntax). This could be useful if ruby-plsql is used without ActiveRecord (e.g. for writing PL/SQL unit tests):
|
61
|
+
|
62
|
+
# insert record in table
|
63
|
+
employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31) }
|
64
|
+
plsql.employees.insert employee # INSERT INTO employees VALUES (1, 'First', 'Last', ...)
|
65
|
+
|
66
|
+
# insert many records
|
67
|
+
employees = [employee1, employee2, ... ] # array of many Hashes
|
68
|
+
plsql.employees.insert employees
|
69
|
+
|
70
|
+
# select one record
|
71
|
+
plsql.employees.first # SELECT * FROM employees
|
72
|
+
# fetch first row => {:employee_id => ..., :first_name => '...', ...}
|
73
|
+
plsql.employees.first(:employee_id => 1) # SELECT * FROM employees WHERE employee_id = 1
|
74
|
+
plsql.employees.first("WHERE employee_id = 1")
|
75
|
+
plsql.employees.first("WHERE employee_id = :employee_id", 1)
|
76
|
+
|
77
|
+
# select many records
|
78
|
+
plsql.employees.all # => [{...}, {...}, ...]
|
79
|
+
plsql.employees.all(:order_by => :employee_id)
|
80
|
+
plsql.employees.all("WHERE employee_id > :employee_id", 5)
|
81
|
+
|
82
|
+
# count records
|
83
|
+
plsql.employees.count # SELECT COUNT(*) FROM employees
|
84
|
+
plsql.employees.count("WHERE employee_id > :employee_id", 5)
|
85
|
+
|
86
|
+
# update records
|
87
|
+
plsql.employees.update(:first_name => 'Second', :where => {:employee_id => 1})
|
88
|
+
# UPDATE employees SET first_name = 'Second' WHERE employee_id = 1
|
89
|
+
|
90
|
+
# delete records
|
91
|
+
plsql.employees.delete(:employee_id => 1) # DELETE FROM employees WHERE employee_id = 1
|
92
|
+
|
93
|
+
# select from sequences
|
94
|
+
plsql.employees_seq.nextval # SELECT employees_seq.NEXTVAL FROM dual
|
95
|
+
plsql.employees_seq.currval # SELECT employees_seq.CURRVAL FROM dual
|
96
|
+
|
97
|
+
|
98
|
+
=== Usage with Rails:
|
37
99
|
|
38
100
|
If using with Rails then include in initializer file:
|
39
101
|
|
40
|
-
plsql.activerecord_class = ActiveRecord::Base
|
102
|
+
plsql.activerecord_class = ActiveRecord::Base
|
41
103
|
|
42
104
|
and then you do not need to specify plsql.connection (this is also safer when ActiveRecord reestablishes connection to database).
|
43
105
|
|
44
106
|
== REQUIREMENTS:
|
45
107
|
|
46
|
-
Ruby 1.8 MRI
|
47
|
-
* Requires ruby-oci8 library
|
48
|
-
|
49
|
-
* Requires
|
50
|
-
JRuby
|
51
|
-
* Requires Oracle JDBC driver (ojdbc14.jar should be somewhere in PATH) to connect to Oracle
|
108
|
+
Ruby 1.8.6/1.8.7 MRI / Ruby 1.9.1 YARV
|
109
|
+
* Requires ruby-oci8 library (please use version 2.0.3 or later)
|
110
|
+
JRuby 1.3/1.4
|
111
|
+
* Requires Oracle JDBC driver (ojdbc14.jar should be somewhere in PATH or should be available for Java class loader)
|
52
112
|
|
53
113
|
== INSTALL:
|
54
114
|
|
55
|
-
* sudo gem install ruby-plsql
|
115
|
+
* [sudo] gem install ruby-plsql
|
116
|
+
|
117
|
+
In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver to $JRUBY_HOME/lib (for JRuby).
|
118
|
+
|
119
|
+
== LINKS
|
120
|
+
|
121
|
+
* Source code: http://github.com/rsim/ruby-plsql
|
122
|
+
* Bug reports / Feature requests: http://github.com/rsim/ruby-plsql/issues
|
123
|
+
* Discuss at oracle_enhanced adapter group: http://groups.google.com/group/oracle-enhanced
|
56
124
|
|
57
125
|
== CONTRIBUTORS:
|
58
126
|
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ruby-plsql"
|
8
|
+
gem.summary = "Ruby API for calling Oracle PL/SQL procedures."
|
9
|
+
gem.description = <<-EOS
|
10
|
+
ruby-plsql gem provides simple Ruby API for calling Oracle PL/SQL procedures.
|
11
|
+
It could be used both for accessing Oracle PL/SQL API procedures in legacy applications
|
12
|
+
as well as it could be used to create PL/SQL unit tests using Ruby testing libraries.
|
13
|
+
EOS
|
14
|
+
gem.email = "raimonds.simanovskis@gmail.com"
|
15
|
+
gem.homepage = "http://github.com/rsim/ruby-plsql"
|
16
|
+
gem.authors = ["Raimonds Simanovskis"]
|
17
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
18
|
+
gem.extra_rdoc_files = ['README.rdoc']
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :spec => :check_dependencies
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'doc'
|
46
|
+
rdoc.title = "ruby-plsql #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.0
|
data/lib/plsql/connection.rb
CHANGED
@@ -3,13 +3,13 @@ module PLSQL
|
|
3
3
|
attr_reader :raw_driver
|
4
4
|
attr_reader :activerecord_class
|
5
5
|
|
6
|
-
def initialize(raw_drv, raw_conn, ar_class = nil)
|
6
|
+
def initialize(raw_drv, raw_conn, ar_class = nil) #:nodoc:
|
7
7
|
@raw_driver = raw_drv
|
8
8
|
@raw_connection = raw_conn
|
9
9
|
@activerecord_class = ar_class
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.create(raw_conn, ar_class = nil)
|
12
|
+
def self.create(raw_conn, ar_class = nil) #:nodoc:
|
13
13
|
if ar_class && !(defined?(::ActiveRecord) && [ar_class, ar_class.superclass].include?(::ActiveRecord::Base))
|
14
14
|
raise ArgumentError, "Wrong ActiveRecord class"
|
15
15
|
end
|
@@ -24,6 +24,7 @@ module PLSQL
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
# Returns OCI8 or JDBC connection
|
27
28
|
def raw_connection
|
28
29
|
if @activerecord_class
|
29
30
|
@activerecord_class.connection.raw_connection
|
@@ -32,50 +33,123 @@ module PLSQL
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
36
|
+
# Is it OCI8 connection
|
35
37
|
def oci?
|
36
38
|
@raw_driver == :oci
|
37
39
|
end
|
38
40
|
|
41
|
+
# Is it JDBC connection
|
39
42
|
def jdbc?
|
40
43
|
@raw_driver == :jdbc
|
41
44
|
end
|
42
45
|
|
43
|
-
def logoff
|
46
|
+
def logoff #:nodoc:
|
44
47
|
raise NoMethodError, "Not implemented for this raw driver"
|
45
48
|
end
|
46
49
|
|
47
|
-
def commit
|
50
|
+
def commit #:nodoc:
|
48
51
|
raise NoMethodError, "Not implemented for this raw driver"
|
49
52
|
end
|
50
53
|
|
51
|
-
def rollback
|
54
|
+
def rollback #:nodoc:
|
52
55
|
raise NoMethodError, "Not implemented for this raw driver"
|
53
56
|
end
|
54
57
|
|
58
|
+
# Current autocommit mode (true or false)
|
55
59
|
def autocommit?
|
56
60
|
raise NoMethodError, "Not implemented for this raw driver"
|
57
61
|
end
|
58
62
|
|
63
|
+
# Set autocommit mode (true or false)
|
59
64
|
def autocommit=(value)
|
60
65
|
raise NoMethodError, "Not implemented for this raw driver"
|
61
66
|
end
|
62
67
|
|
63
|
-
def select_first(sql, *bindvars)
|
64
|
-
|
68
|
+
def select_first(sql, *bindvars) #:nodoc:
|
69
|
+
cursor = cursor_from_query(self, sql, *bindvars)
|
70
|
+
cursor.fetch
|
71
|
+
ensure
|
72
|
+
cursor.close rescue nil
|
65
73
|
end
|
66
74
|
|
67
|
-
def
|
68
|
-
|
75
|
+
def select_hash_first(sql, *bindvars) #:nodoc:
|
76
|
+
cursor = cursor_from_query(self, sql, *bindvars)
|
77
|
+
cursor.fetch_hash
|
78
|
+
ensure
|
79
|
+
cursor.close rescue nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def select_all(sql, *bindvars, &block) #:nodoc:
|
83
|
+
cursor = cursor_from_query(self, sql, *bindvars)
|
84
|
+
results = []
|
85
|
+
row_count = 0
|
86
|
+
while row = cursor.fetch
|
87
|
+
if block_given?
|
88
|
+
yield(row)
|
89
|
+
row_count += 1
|
90
|
+
else
|
91
|
+
results << row
|
92
|
+
end
|
93
|
+
end
|
94
|
+
block_given? ? row_count : results
|
95
|
+
ensure
|
96
|
+
cursor.close rescue nil
|
69
97
|
end
|
70
98
|
|
71
|
-
def
|
99
|
+
def select_hash_all(sql, *bindvars, &block) #:nodoc:
|
100
|
+
cursor = cursor_from_query(self, sql, *bindvars)
|
101
|
+
results = []
|
102
|
+
row_count = 0
|
103
|
+
while row = cursor.fetch_hash
|
104
|
+
if block_given?
|
105
|
+
yield(row)
|
106
|
+
row_count += 1
|
107
|
+
else
|
108
|
+
results << row
|
109
|
+
end
|
110
|
+
end
|
111
|
+
block_given? ? row_count : results
|
112
|
+
ensure
|
113
|
+
cursor.close rescue nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def exec(sql, *bindvars) #:nodoc:
|
72
117
|
raise NoMethodError, "Not implemented for this raw driver"
|
73
118
|
end
|
74
119
|
|
75
|
-
def parse(sql)
|
120
|
+
def parse(sql) #:nodoc:
|
76
121
|
raise NoMethodError, "Not implemented for this raw driver"
|
77
122
|
end
|
78
123
|
|
124
|
+
def arrays_to_hash(keys, values) #:nodoc:
|
125
|
+
(0...keys.size).inject({}) { |hash, i| hash[keys[i]] = values[i]; hash }
|
126
|
+
end
|
127
|
+
|
128
|
+
module CursorCommon
|
129
|
+
# Fetch all rows from cursor, each row as array of values
|
130
|
+
def fetch_all
|
131
|
+
rows = []
|
132
|
+
while (row = fetch)
|
133
|
+
rows << row
|
134
|
+
end
|
135
|
+
rows
|
136
|
+
end
|
137
|
+
|
138
|
+
# Fetch all rows from cursor, each row as hash {:column => value, ...}
|
139
|
+
def fetch_hash_all
|
140
|
+
rows = []
|
141
|
+
while (row = fetch_hash)
|
142
|
+
rows << row
|
143
|
+
end
|
144
|
+
rows
|
145
|
+
end
|
146
|
+
|
147
|
+
# Fetch row from cursor as hash {:column => value, ...}
|
148
|
+
def fetch_hash
|
149
|
+
(row = fetch) && @connection.arrays_to_hash(fields, row)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
79
153
|
end
|
80
154
|
|
81
155
|
end
|
@@ -1,5 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require "java"
|
3
|
+
require "jruby"
|
4
|
+
|
5
|
+
# ojdbc14.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
|
6
|
+
|
7
|
+
ojdbc_jar = "ojdbc14.jar"
|
8
|
+
|
9
|
+
unless ENV_JAVA['java.class.path'] =~ Regexp.new(ojdbc_jar)
|
10
|
+
# On Unix environment variable should be PATH, on Windows it is sometimes Path
|
11
|
+
env_path = ENV["PATH"] || ENV["Path"] || ''
|
12
|
+
if ojdbc_jar_path = env_path.split(/[:;]/).concat($LOAD_PATH).find{|d| File.exists?(File.join(d,ojdbc_jar))}
|
13
|
+
require File.join(ojdbc_jar_path,ojdbc_jar)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
java.sql.DriverManager.registerDriver Java::oracle.jdbc.driver.OracleDriver.new
|
18
|
+
|
19
|
+
# set tns_admin property from TNS_ADMIN environment variable
|
20
|
+
if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
|
21
|
+
java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
|
22
|
+
end
|
23
|
+
|
24
|
+
rescue LoadError, NameError
|
25
|
+
# JDBC driver is unavailable.
|
26
|
+
error_message = "ERROR: ruby-plsql could not load Oracle JDBC driver. "+
|
27
|
+
"Please install ojdbc14.jar library."
|
28
|
+
STDERR.puts error_message
|
29
|
+
raise LoadError
|
30
|
+
end
|
31
|
+
|
1
32
|
module PLSQL
|
2
|
-
class JDBCConnection < Connection
|
33
|
+
class JDBCConnection < Connection #:nodoc:
|
3
34
|
def logoff
|
4
35
|
raw_connection.close
|
5
36
|
true
|
@@ -23,47 +54,6 @@ module PLSQL
|
|
23
54
|
raw_connection.setAutoCommit(value)
|
24
55
|
end
|
25
56
|
|
26
|
-
def select_first(sql, *bindvars)
|
27
|
-
stmt = prepare_statement(sql, *bindvars)
|
28
|
-
rset = stmt.executeQuery
|
29
|
-
metadata = rset.getMetaData
|
30
|
-
column_count = metadata.getColumnCount
|
31
|
-
if rset.next
|
32
|
-
(1..column_count).map do |i|
|
33
|
-
get_ruby_value_from_result_set(rset,i,metadata.getColumnTypeName(i))
|
34
|
-
end
|
35
|
-
else
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
ensure
|
39
|
-
rset.close rescue nil
|
40
|
-
stmt.close rescue nil
|
41
|
-
end
|
42
|
-
|
43
|
-
def select_all(sql, *bindvars, &block)
|
44
|
-
stmt = prepare_statement(sql, *bindvars)
|
45
|
-
results = []
|
46
|
-
row_count = 0
|
47
|
-
rset = stmt.executeQuery
|
48
|
-
metadata = rset.getMetaData
|
49
|
-
column_count = metadata.getColumnCount
|
50
|
-
while rset.next
|
51
|
-
row_with_typecast = (1..column_count).map do |i|
|
52
|
-
get_ruby_value_from_result_set(rset,i,metadata.getColumnTypeName(i))
|
53
|
-
end
|
54
|
-
if block_given?
|
55
|
-
yield(row_with_typecast)
|
56
|
-
row_count += 1
|
57
|
-
else
|
58
|
-
results << row_with_typecast
|
59
|
-
end
|
60
|
-
end
|
61
|
-
block_given? ? row_count : results
|
62
|
-
ensure
|
63
|
-
rset.close rescue nil
|
64
|
-
stmt.close rescue nil
|
65
|
-
end
|
66
|
-
|
67
57
|
def exec(sql, *bindvars)
|
68
58
|
cs = prepare_call(sql, *bindvars)
|
69
59
|
cs.execute
|
@@ -72,9 +62,9 @@ module PLSQL
|
|
72
62
|
cs.close rescue nil
|
73
63
|
end
|
74
64
|
|
75
|
-
class
|
65
|
+
class CallableStatement #:nodoc:
|
76
66
|
|
77
|
-
def initialize(
|
67
|
+
def initialize(conn, sql)
|
78
68
|
@sql = sql
|
79
69
|
@connection = conn
|
80
70
|
@params = sql.scan(/\:\w+/)
|
@@ -83,12 +73,19 @@ module PLSQL
|
|
83
73
|
@statement = @connection.prepare_call(sql)
|
84
74
|
end
|
85
75
|
|
86
|
-
def bind_param(
|
87
|
-
@connection.
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@
|
76
|
+
def bind_param(arg, value, metadata)
|
77
|
+
type, length = @connection.plsql_to_ruby_data_type(metadata)
|
78
|
+
ora_value = @connection.ruby_value_to_ora_value(value, type, metadata)
|
79
|
+
@connection.set_bind_variable(@statement, arg, ora_value, type, length, metadata)
|
80
|
+
if metadata[:in_out] =~ /OUT/
|
81
|
+
@out_types[arg] = type || ora_value.class
|
82
|
+
@out_index[arg] = bind_param_index(arg)
|
83
|
+
if ['TABLE','VARRAY','OBJECT','REF CURSOR'].include?(metadata[:data_type])
|
84
|
+
@statement.registerOutParameter(@out_index[arg], @connection.get_java_sql_type(ora_value,type),
|
85
|
+
metadata[:sql_type_name])
|
86
|
+
else
|
87
|
+
@statement.registerOutParameter(@out_index[arg],@connection.get_java_sql_type(ora_value,type))
|
88
|
+
end
|
92
89
|
end
|
93
90
|
end
|
94
91
|
|
@@ -97,7 +94,7 @@ module PLSQL
|
|
97
94
|
end
|
98
95
|
|
99
96
|
def [](key)
|
100
|
-
@connection.get_bind_variable(@statement, @out_index[key], @out_types[key])
|
97
|
+
@connection.ora_value_to_ruby_value(@connection.get_bind_variable(@statement, @out_index[key], @out_types[key]))
|
101
98
|
end
|
102
99
|
|
103
100
|
def close
|
@@ -113,14 +110,68 @@ module PLSQL
|
|
113
110
|
end
|
114
111
|
end
|
115
112
|
|
113
|
+
class Cursor #:nodoc:
|
114
|
+
include Connection::CursorCommon
|
115
|
+
|
116
|
+
attr_reader :result_set
|
117
|
+
attr_accessor :statement
|
118
|
+
|
119
|
+
def initialize(conn, result_set)
|
120
|
+
@connection = conn
|
121
|
+
@result_set = result_set
|
122
|
+
@metadata = @result_set.getMetaData
|
123
|
+
@column_count = @metadata.getColumnCount
|
124
|
+
@column_type_names = [nil] # column numbering starts at 1
|
125
|
+
(1..@column_count).each do |i|
|
126
|
+
@column_type_names << {:type_name => @metadata.getColumnTypeName(i), :sql_type => @metadata.getColumnType(i)}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.new_from_query(conn, sql, *bindvars)
|
131
|
+
stmt = conn.prepare_statement(sql, *bindvars)
|
132
|
+
cursor = Cursor.new(conn, stmt.executeQuery)
|
133
|
+
cursor.statement = stmt
|
134
|
+
cursor
|
135
|
+
rescue
|
136
|
+
# in case of any error close statement
|
137
|
+
stmt.close rescue nil
|
138
|
+
raise
|
139
|
+
end
|
140
|
+
|
141
|
+
def fetch
|
142
|
+
if @result_set.next
|
143
|
+
(1..@column_count).map do |i|
|
144
|
+
@connection.get_ruby_value_from_result_set(@result_set, i, @column_type_names[i])
|
145
|
+
end
|
146
|
+
else
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def fields
|
152
|
+
@fields ||= (1..@column_count).map do |i|
|
153
|
+
@metadata.getColumnName(i).downcase.to_sym
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def close
|
158
|
+
@result_set.close
|
159
|
+
@statement.close if @statement
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
116
163
|
def parse(sql)
|
117
|
-
|
164
|
+
CallableStatement.new(self, sql)
|
165
|
+
end
|
166
|
+
|
167
|
+
def cursor_from_query(sql, *bindvars)
|
168
|
+
Cursor.new_from_query(sql, *bindvars)
|
118
169
|
end
|
119
170
|
|
120
171
|
def prepare_statement(sql, *bindvars)
|
121
172
|
stmt = raw_connection.prepareStatement(sql)
|
122
173
|
bindvars.each_with_index do |bv, i|
|
123
|
-
set_bind_variable(stmt, i+1, bv)
|
174
|
+
set_bind_variable(stmt, i+1, ruby_value_to_ora_value(bv))
|
124
175
|
end
|
125
176
|
stmt
|
126
177
|
end
|
@@ -133,118 +184,129 @@ module PLSQL
|
|
133
184
|
stmt
|
134
185
|
end
|
135
186
|
|
187
|
+
RUBY_CLASS_TO_SQL_TYPE = {
|
188
|
+
Fixnum => java.sql.Types::INTEGER,
|
189
|
+
Bignum => java.sql.Types::INTEGER,
|
190
|
+
Integer => java.sql.Types::INTEGER,
|
191
|
+
Float => java.sql.Types::FLOAT,
|
192
|
+
BigDecimal => java.sql.Types::NUMERIC,
|
193
|
+
String => java.sql.Types::VARCHAR,
|
194
|
+
Java::OracleSql::CLOB => Java::oracle.jdbc.OracleTypes::CLOB,
|
195
|
+
Java::OracleSql::BLOB => Java::oracle.jdbc.OracleTypes::BLOB,
|
196
|
+
Date => java.sql.Types::DATE,
|
197
|
+
Time => java.sql.Types::DATE,
|
198
|
+
DateTime => java.sql.Types::DATE,
|
199
|
+
Java::OracleSql::ARRAY => Java::oracle.jdbc.OracleTypes::ARRAY,
|
200
|
+
Array => Java::oracle.jdbc.OracleTypes::ARRAY,
|
201
|
+
Java::OracleSql::STRUCT => Java::oracle.jdbc.OracleTypes::STRUCT,
|
202
|
+
Hash => Java::oracle.jdbc.OracleTypes::STRUCT,
|
203
|
+
java.sql.ResultSet => Java::oracle.jdbc.OracleTypes::CURSOR,
|
204
|
+
}
|
205
|
+
|
206
|
+
SQL_TYPE_TO_RUBY_CLASS = {
|
207
|
+
java.sql.Types::CHAR => String,
|
208
|
+
java.sql.Types::VARCHAR => String,
|
209
|
+
java.sql.Types::NUMERIC => BigDecimal,
|
210
|
+
java.sql.Types::DATE => Time,
|
211
|
+
java.sql.Types::TIMESTAMP => Time,
|
212
|
+
Java::oracle.jdbc.OracleTypes::TIMESTAMPTZ => Time,
|
213
|
+
Java::oracle.jdbc.OracleTypes::TIMESTAMPLTZ => Time,
|
214
|
+
java.sql.Types::BLOB => String,
|
215
|
+
java.sql.Types::CLOB => String,
|
216
|
+
java.sql.Types::ARRAY => Java::OracleSql::ARRAY,
|
217
|
+
java.sql.Types::STRUCT => Java::OracleSql::STRUCT,
|
218
|
+
Java::oracle.jdbc.OracleTypes::CURSOR => java.sql.ResultSet
|
219
|
+
}
|
220
|
+
|
136
221
|
def get_java_sql_type(value, type)
|
137
|
-
|
138
|
-
when 'Fixnum', 'Bignum', 'Integer'
|
139
|
-
java.sql.Types::INTEGER
|
140
|
-
when 'Float'
|
141
|
-
java.sql.Types::FLOAT
|
142
|
-
when 'BigDecimal'
|
143
|
-
java.sql.Types::NUMERIC
|
144
|
-
when 'String'
|
145
|
-
java.sql.Types::VARCHAR
|
146
|
-
when 'Java::OracleSql::CLOB'
|
147
|
-
Java::oracle.jdbc.OracleTypes::CLOB
|
148
|
-
when 'Java::OracleSql::BLOB'
|
149
|
-
Java::oracle.jdbc.OracleTypes::BLOB
|
150
|
-
when 'Date'
|
151
|
-
java.sql.Types::DATE
|
152
|
-
when 'Time'
|
153
|
-
java.sql.Types::DATE
|
154
|
-
when 'DateTime'
|
155
|
-
java.sql.Types::DATE
|
156
|
-
else
|
157
|
-
java.sql.Types::VARCHAR
|
158
|
-
end
|
222
|
+
RUBY_CLASS_TO_SQL_TYPE[type || value.class] || java.sql.Types::VARCHAR
|
159
223
|
end
|
160
224
|
|
161
|
-
def set_bind_variable(stmt, i, value, type=nil, length=nil)
|
225
|
+
def set_bind_variable(stmt, i, value, type=nil, length=nil, metadata={})
|
162
226
|
key = i.kind_of?(Integer) ? nil : i.to_s.gsub(':','')
|
163
|
-
|
164
|
-
|
227
|
+
type_symbol = (!value.nil? && type ? type : value.class).to_s.to_sym
|
228
|
+
case type_symbol
|
229
|
+
when :Fixnum, :Bignum, :Integer
|
165
230
|
stmt.send("setInt#{key && "AtName"}", key || i, value)
|
166
|
-
when
|
231
|
+
when :Float
|
167
232
|
stmt.send("setFloat#{key && "AtName"}", key || i, value)
|
168
|
-
when 'BigDecimal'
|
169
|
-
stmt.send("setBigDecimal#{key && "AtName"}", key || i,
|
170
|
-
when
|
233
|
+
when :BigDecimal, :'Java::JavaMath::BigDecimal'
|
234
|
+
stmt.send("setBigDecimal#{key && "AtName"}", key || i, value)
|
235
|
+
when :String
|
171
236
|
stmt.send("setString#{key && "AtName"}", key || i, value)
|
172
|
-
when 'Java::OracleSql::CLOB'
|
237
|
+
when :'Java::OracleSql::CLOB'
|
173
238
|
stmt.send("setClob#{key && "AtName"}", key || i, value)
|
174
|
-
when 'Java::OracleSql::BLOB'
|
239
|
+
when :'Java::OracleSql::BLOB'
|
175
240
|
stmt.send("setBlob#{key && "AtName"}", key || i, value)
|
176
|
-
when
|
177
|
-
stmt.send("setDATE#{key && "AtName"}", key || i,
|
178
|
-
when
|
179
|
-
|
241
|
+
when :Date, :Time, :DateTime, :'Java::OracleSql::DATE'
|
242
|
+
stmt.send("setDATE#{key && "AtName"}", key || i, value)
|
243
|
+
when :NilClass
|
244
|
+
if ['TABLE', 'VARRAY', 'OBJECT'].include?(metadata[:data_type])
|
245
|
+
stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type),
|
246
|
+
metadata[:sql_type_name])
|
247
|
+
elsif metadata[:data_type] == 'REF CURSOR'
|
248
|
+
# TODO: cannot bind NULL value to cursor parameter, getting error
|
249
|
+
# java.sql.SQLException: Unsupported feature: sqlType=-10
|
250
|
+
# Currently do nothing and assume that NULL values will not be passed to IN parameters
|
251
|
+
# If cursor is IN/OUT or OUT parameter then it should work
|
252
|
+
else
|
253
|
+
stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type))
|
254
|
+
end
|
255
|
+
when :'Java::OracleSql::ARRAY'
|
256
|
+
stmt.send("setARRAY#{key && "AtName"}", key || i, value)
|
257
|
+
when :'Java::OracleSql::STRUCT'
|
258
|
+
stmt.send("setSTRUCT#{key && "AtName"}", key || i, value)
|
259
|
+
when :'Java::JavaSql::ResultSet'
|
260
|
+
# TODO: cannot find how to pass cursor parameter from JDBC
|
261
|
+
# setCursor is giving exception java.sql.SQLException: Unsupported feature
|
262
|
+
stmt.send("setCursor#{key && "AtName"}", key || i, value)
|
263
|
+
else
|
264
|
+
raise ArgumentError, "Don't know how to bind variable with type #{type_symbol}"
|
180
265
|
end
|
181
266
|
end
|
182
267
|
|
183
268
|
def get_bind_variable(stmt, i, type)
|
184
|
-
case type.to_s
|
185
|
-
when
|
269
|
+
case type.to_s.to_sym
|
270
|
+
when :Fixnum, :Bignum, :Integer
|
186
271
|
stmt.getInt(i)
|
187
|
-
when
|
272
|
+
when :Float
|
188
273
|
stmt.getFloat(i)
|
189
|
-
when
|
274
|
+
when :BigDecimal
|
190
275
|
bd = stmt.getBigDecimal(i)
|
191
276
|
bd && BigDecimal.new(bd.to_s)
|
192
|
-
when
|
277
|
+
when :String
|
193
278
|
stmt.getString(i)
|
194
|
-
when 'Java::OracleSql::CLOB'
|
279
|
+
when :'Java::OracleSql::CLOB'
|
195
280
|
stmt.getClob(i)
|
196
|
-
when 'Java::OracleSql::BLOB'
|
281
|
+
when :'Java::OracleSql::BLOB'
|
197
282
|
stmt.getBlob(i)
|
198
|
-
when
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
283
|
+
when :Date, :Time, :DateTime
|
284
|
+
stmt.getDATE(i)
|
285
|
+
when :'Java::OracleSql::ARRAY'
|
286
|
+
stmt.getArray(i)
|
287
|
+
when :'Java::OracleSql::STRUCT'
|
288
|
+
stmt.getSTRUCT(i)
|
289
|
+
when :'Java::JavaSql::ResultSet'
|
290
|
+
stmt.getCursor(i)
|
206
291
|
end
|
207
292
|
end
|
208
293
|
|
209
|
-
def get_ruby_value_from_result_set(rset, i,
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
when "CLOB"
|
214
|
-
ora_value_to_ruby_value(rset.getClob(i))
|
215
|
-
when "BLOB"
|
216
|
-
ora_value_to_ruby_value(rset.getBlob(i))
|
217
|
-
when "NUMBER"
|
218
|
-
d = rset.getBigDecimal(i)
|
219
|
-
if d.nil?
|
220
|
-
nil
|
221
|
-
elsif d.scale == 0
|
222
|
-
d.toBigInteger+0
|
223
|
-
else
|
224
|
-
BigDecimal(d.toString)
|
225
|
-
end
|
226
|
-
when "DATE"
|
227
|
-
if dt = rset.getDATE(i)
|
228
|
-
d = dt.dateValue
|
229
|
-
t = dt.timeValue
|
230
|
-
Time.send(plsql.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
|
231
|
-
else
|
232
|
-
nil
|
233
|
-
end
|
234
|
-
when /^TIMESTAMP/
|
235
|
-
ts = rset.getTimestamp(i)
|
236
|
-
ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
|
237
|
-
ts.nanos / 1000)
|
238
|
-
else
|
239
|
-
nil
|
240
|
-
end
|
294
|
+
def get_ruby_value_from_result_set(rset, i, metadata)
|
295
|
+
ruby_type = SQL_TYPE_TO_RUBY_CLASS[metadata[:sql_type]]
|
296
|
+
ora_value = get_bind_variable(rset, i, ruby_type)
|
297
|
+
result_new = ora_value_to_ruby_value(ora_value)
|
241
298
|
end
|
242
|
-
|
243
|
-
def
|
299
|
+
|
300
|
+
def result_set_to_ruby_data_type(column_type, column_type_name)
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
def plsql_to_ruby_data_type(metadata)
|
305
|
+
data_type, data_length = metadata[:data_type], metadata[:data_length]
|
244
306
|
case data_type
|
245
|
-
when "VARCHAR2"
|
307
|
+
when "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
|
246
308
|
[String, data_length || 32767]
|
247
|
-
when "CLOB"
|
309
|
+
when "CLOB", "NCLOB"
|
248
310
|
[Java::OracleSql::CLOB, nil]
|
249
311
|
when "BLOB"
|
250
312
|
[Java::OracleSql::BLOB, nil]
|
@@ -252,73 +314,163 @@ module PLSQL
|
|
252
314
|
[BigDecimal, nil]
|
253
315
|
when "DATE"
|
254
316
|
[Time, nil]
|
255
|
-
when "TIMESTAMP"
|
317
|
+
when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
|
256
318
|
[Time, nil]
|
319
|
+
when "TABLE", "VARRAY"
|
320
|
+
[Java::OracleSql::ARRAY, nil]
|
321
|
+
when "OBJECT"
|
322
|
+
[Java::OracleSql::STRUCT, nil]
|
323
|
+
when "REF CURSOR"
|
324
|
+
[java.sql.ResultSet, nil]
|
257
325
|
else
|
258
326
|
[String, 32767]
|
259
327
|
end
|
260
328
|
end
|
261
329
|
|
262
|
-
def ruby_value_to_ora_value(
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
330
|
+
def ruby_value_to_ora_value(value, type=nil, metadata={})
|
331
|
+
type ||= value.class
|
332
|
+
case type.to_s.to_sym
|
333
|
+
when :Fixnum, :String
|
334
|
+
value
|
335
|
+
when :BigDecimal
|
336
|
+
case value
|
337
|
+
when TrueClass
|
338
|
+
java_bigdecimal(1)
|
339
|
+
when FalseClass
|
340
|
+
java_bigdecimal(0)
|
341
|
+
else
|
342
|
+
java_bigdecimal(value)
|
343
|
+
end
|
344
|
+
when :Time, :Date, :DateTime
|
345
|
+
case value
|
267
346
|
when DateTime
|
268
|
-
Time.send(plsql.default_timezone,
|
347
|
+
java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, value.hour, value.min, value.sec))
|
269
348
|
when Date
|
270
|
-
Time.send(plsql.default_timezone,
|
349
|
+
java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, 0, 0, 0))
|
271
350
|
else
|
272
|
-
|
351
|
+
java_date(value)
|
273
352
|
end
|
274
|
-
|
275
|
-
if
|
353
|
+
when :'Java::OracleSql::CLOB'
|
354
|
+
if value
|
276
355
|
clob = Java::OracleSql::CLOB.createTemporary(raw_connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
|
277
|
-
clob.setString(1,
|
356
|
+
clob.setString(1, value)
|
278
357
|
clob
|
279
358
|
else
|
280
359
|
Java::OracleSql::CLOB.getEmptyCLOB
|
281
360
|
end
|
282
|
-
|
283
|
-
if
|
361
|
+
when :'Java::OracleSql::BLOB'
|
362
|
+
if value
|
284
363
|
blob = Java::OracleSql::BLOB.createTemporary(raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
|
285
|
-
blob.setBytes(1,
|
364
|
+
blob.setBytes(1, value.to_java_bytes)
|
286
365
|
blob
|
287
366
|
else
|
288
367
|
Java::OracleSql::BLOB.getEmptyBLOB
|
289
368
|
end
|
369
|
+
when :'Java::OracleSql::ARRAY'
|
370
|
+
if value
|
371
|
+
raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
|
372
|
+
descriptor = Java::OracleSql::ArrayDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
|
373
|
+
elem_type = descriptor.getBaseType
|
374
|
+
elem_type_name = descriptor.getBaseName
|
375
|
+
elem_list = value.map do |elem|
|
376
|
+
case elem_type
|
377
|
+
when Java::oracle.jdbc.OracleTypes::ARRAY
|
378
|
+
ruby_value_to_ora_value(elem, Java::OracleSql::ARRAY, :sql_type_name => elem_type_name)
|
379
|
+
when Java::oracle.jdbc.OracleTypes::STRUCT
|
380
|
+
ruby_value_to_ora_value(elem, Java::OracleSql::STRUCT, :sql_type_name => elem_type_name)
|
381
|
+
else
|
382
|
+
ruby_value_to_ora_value(elem)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
Java::OracleSql::ARRAY.new(descriptor, raw_connection, elem_list.to_java)
|
386
|
+
end
|
387
|
+
when :'Java::OracleSql::STRUCT'
|
388
|
+
if value
|
389
|
+
raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
|
390
|
+
descriptor = Java::OracleSql::StructDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
|
391
|
+
struct_metadata = descriptor.getMetaData
|
392
|
+
struct_fields = (1..descriptor.getLength).inject({}) do |hash, i|
|
393
|
+
hash[struct_metadata.getColumnName(i).downcase.to_sym] =
|
394
|
+
{:type => struct_metadata.getColumnType(i), :type_name => struct_metadata.getColumnTypeName(i)}
|
395
|
+
hash
|
396
|
+
end
|
397
|
+
object_attrs = java.util.HashMap.new
|
398
|
+
value.each do |key, attr_value|
|
399
|
+
raise ArgumentError, "Wrong object type field passed to PL/SQL procedure" unless (field = struct_fields[key])
|
400
|
+
case field[:type]
|
401
|
+
when Java::oracle.jdbc.OracleTypes::ARRAY
|
402
|
+
# nested collection
|
403
|
+
object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::ARRAY, :sql_type_name => field[:type_name]))
|
404
|
+
when Java::oracle.jdbc.OracleTypes::STRUCT
|
405
|
+
# nested object type
|
406
|
+
object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::STRUCT, :sql_type_name => field[:type_name]))
|
407
|
+
else
|
408
|
+
object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value))
|
409
|
+
end
|
410
|
+
end
|
411
|
+
Java::OracleSql::STRUCT.new(descriptor, raw_connection, object_attrs)
|
412
|
+
end
|
413
|
+
when :'Java::JavaSql::ResultSet'
|
414
|
+
if value
|
415
|
+
value.result_set
|
416
|
+
end
|
290
417
|
else
|
291
|
-
|
418
|
+
value
|
292
419
|
end
|
293
420
|
end
|
294
421
|
|
295
|
-
def ora_value_to_ruby_value(
|
296
|
-
case
|
422
|
+
def ora_value_to_ruby_value(value)
|
423
|
+
case value
|
297
424
|
when Float, BigDecimal
|
298
|
-
ora_number_to_ruby_number(
|
425
|
+
ora_number_to_ruby_number(value)
|
426
|
+
when Java::JavaMath::BigDecimal
|
427
|
+
value && ora_number_to_ruby_number(BigDecimal.new(value.to_s))
|
428
|
+
when Java::OracleSql::DATE
|
429
|
+
if value
|
430
|
+
d = value.dateValue
|
431
|
+
t = value.timeValue
|
432
|
+
Time.send(plsql.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
|
433
|
+
end
|
299
434
|
when Java::OracleSql::CLOB
|
300
|
-
if
|
435
|
+
if value.isEmptyLob
|
301
436
|
nil
|
302
437
|
else
|
303
|
-
|
438
|
+
value.getSubString(1, value.length)
|
304
439
|
end
|
305
440
|
when Java::OracleSql::BLOB
|
306
|
-
if
|
441
|
+
if value.isEmptyLob
|
307
442
|
nil
|
308
443
|
else
|
309
|
-
String.from_java_bytes(
|
444
|
+
String.from_java_bytes(value.getBytes(1, value.length))
|
310
445
|
end
|
446
|
+
when Java::OracleSql::ARRAY
|
447
|
+
value.getArray.map{|e| ora_value_to_ruby_value(e)}
|
448
|
+
when Java::OracleSql::STRUCT
|
449
|
+
descriptor = value.getDescriptor
|
450
|
+
struct_metadata = descriptor.getMetaData
|
451
|
+
field_names = (1..descriptor.getLength).map {|i| struct_metadata.getColumnName(i).downcase.to_sym}
|
452
|
+
field_values = value.getAttributes.map{|e| ora_value_to_ruby_value(e)}
|
453
|
+
arrays_to_hash(field_names, field_values)
|
454
|
+
when Java::java.sql.ResultSet
|
455
|
+
Cursor.new(self, value)
|
311
456
|
else
|
312
|
-
|
457
|
+
value
|
313
458
|
end
|
314
459
|
end
|
315
460
|
|
316
461
|
private
|
317
462
|
|
463
|
+
def java_date(value)
|
464
|
+
value && Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
|
465
|
+
end
|
466
|
+
|
467
|
+
def java_bigdecimal(value)
|
468
|
+
value && java.math.BigDecimal.new(value.to_s)
|
469
|
+
end
|
470
|
+
|
318
471
|
def ora_number_to_ruby_number(num)
|
319
472
|
# return BigDecimal instead of Float to avoid rounding errors
|
320
|
-
|
321
|
-
num == (num_to_i = num.to_i) ? num_to_i : BigDecimal.new(num.to_s)
|
473
|
+
num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal.new(num.to_s))
|
322
474
|
end
|
323
475
|
|
324
476
|
end
|