ruby-plsql 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|