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/lib/plsql/oci_connection.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
require "oci8"
|
3
|
+
rescue LoadError
|
4
|
+
# OCI8 driver is unavailable.
|
5
|
+
error_message = "ERROR: ruby-plsql could not load ruby-oci8 library. "+
|
6
|
+
"Please install ruby-oci8 gem."
|
7
|
+
STDERR.puts error_message
|
8
|
+
raise LoadError
|
9
|
+
end
|
10
|
+
|
1
11
|
module PLSQL
|
2
|
-
class OCIConnection < Connection
|
12
|
+
class OCIConnection < Connection #:nodoc:
|
3
13
|
|
4
14
|
def logoff
|
5
15
|
raw_connection.logoff
|
@@ -21,71 +31,80 @@ module PLSQL
|
|
21
31
|
raw_connection.autocommit = value
|
22
32
|
end
|
23
33
|
|
24
|
-
def select_first(sql, *bindvars)
|
25
|
-
cursor = raw_connection.exec(sql, *bindvars)
|
26
|
-
result = cursor.fetch
|
27
|
-
if result
|
28
|
-
result.map { |val| ora_value_to_ruby_value(val) }
|
29
|
-
else
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
ensure
|
33
|
-
cursor.close rescue nil
|
34
|
-
end
|
35
|
-
|
36
|
-
def select_all(sql, *bindvars, &block)
|
37
|
-
cursor = raw_connection.exec(sql, *bindvars)
|
38
|
-
results = []
|
39
|
-
row_count = 0
|
40
|
-
while row = cursor.fetch
|
41
|
-
row_with_typecast = row.map {|val| ora_value_to_ruby_value(val) }
|
42
|
-
if block_given?
|
43
|
-
yield(row_with_typecast)
|
44
|
-
row_count += 1
|
45
|
-
else
|
46
|
-
results << row_with_typecast
|
47
|
-
end
|
48
|
-
end
|
49
|
-
block_given? ? row_count : results
|
50
|
-
ensure
|
51
|
-
cursor.close rescue nil
|
52
|
-
end
|
53
|
-
|
54
34
|
def exec(sql, *bindvars)
|
55
35
|
raw_connection.exec(sql, *bindvars)
|
56
36
|
true
|
57
37
|
end
|
58
38
|
|
59
|
-
class Cursor
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
39
|
+
class Cursor #:nodoc:
|
40
|
+
include Connection::CursorCommon
|
41
|
+
|
42
|
+
# stack of open cursors
|
43
|
+
@@open_cursors = []
|
44
|
+
attr_reader :raw_cursor
|
45
|
+
|
46
|
+
def initialize(conn, raw_cursor)
|
47
|
+
@connection = conn
|
48
|
+
@raw_cursor = raw_cursor
|
49
|
+
@@open_cursors.push self
|
64
50
|
end
|
65
51
|
|
66
|
-
def
|
67
|
-
raw_cursor.
|
52
|
+
def self.new_from_parse(conn, sql)
|
53
|
+
raw_cursor = conn.raw_connection.parse(sql)
|
54
|
+
self.new(conn, raw_cursor)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.new_from_query(conn, sql, *bindvars)
|
58
|
+
Cursor.new(conn, conn.raw_connection.exec(sql, *bindvars))
|
59
|
+
end
|
60
|
+
|
61
|
+
def bind_param(arg, value, metadata)
|
62
|
+
type, length = @connection.plsql_to_ruby_data_type(metadata)
|
63
|
+
ora_value = @connection.ruby_value_to_ora_value(value, type)
|
64
|
+
@raw_cursor.bind_param(arg, ora_value, type, length)
|
68
65
|
end
|
69
66
|
|
70
67
|
def exec(*bindvars)
|
71
|
-
raw_cursor.exec(*bindvars)
|
68
|
+
@raw_cursor.exec(*bindvars)
|
72
69
|
end
|
73
70
|
|
74
71
|
def [](key)
|
75
|
-
raw_cursor[key]
|
72
|
+
@connection.ora_value_to_ruby_value(@raw_cursor[key])
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch
|
76
|
+
row = @raw_cursor.fetch
|
77
|
+
row && row.map{|v| @connection.ora_value_to_ruby_value(v)}
|
78
|
+
end
|
79
|
+
|
80
|
+
def fields
|
81
|
+
@fields ||= @raw_cursor.get_col_names.map{|c| c.downcase.to_sym}
|
82
|
+
end
|
83
|
+
|
84
|
+
def close_raw_cursor
|
85
|
+
@raw_cursor.close
|
76
86
|
end
|
77
87
|
|
78
88
|
def close
|
79
|
-
|
89
|
+
# close all cursors that were created after this one
|
90
|
+
while (open_cursor = @@open_cursors.pop) && !open_cursor.equal?(self)
|
91
|
+
open_cursor.close_raw_cursor
|
92
|
+
end
|
93
|
+
close_raw_cursor
|
80
94
|
end
|
95
|
+
|
81
96
|
end
|
82
97
|
|
83
98
|
def parse(sql)
|
84
|
-
Cursor.
|
99
|
+
Cursor.new_from_parse(self, sql)
|
85
100
|
end
|
86
|
-
|
87
101
|
|
88
|
-
def
|
102
|
+
def cursor_from_query(sql, *bindvars)
|
103
|
+
Cursor.new_from_query(sql, *bindvars)
|
104
|
+
end
|
105
|
+
|
106
|
+
def plsql_to_ruby_data_type(metadata)
|
107
|
+
data_type, data_length = metadata[:data_type], metadata[:data_length]
|
89
108
|
case data_type
|
90
109
|
when "VARCHAR2"
|
91
110
|
[String, data_length || 32767]
|
@@ -99,63 +118,125 @@ module PLSQL
|
|
99
118
|
[DateTime, nil]
|
100
119
|
when "TIMESTAMP"
|
101
120
|
[Time, nil]
|
102
|
-
|
103
|
-
|
121
|
+
when "TABLE", "VARRAY", "OBJECT"
|
122
|
+
# create Ruby class for collection
|
123
|
+
klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
|
124
|
+
unless klass
|
125
|
+
klass = Class.new(OCI8::Object::Base)
|
126
|
+
klass.set_typename metadata[:sql_type_name]
|
127
|
+
end
|
128
|
+
[klass, nil]
|
129
|
+
when "REF CURSOR"
|
130
|
+
[OCI8::Cursor]
|
104
131
|
else
|
105
132
|
[String, 32767]
|
106
133
|
end
|
107
134
|
end
|
108
135
|
|
109
|
-
def ruby_value_to_ora_value(
|
110
|
-
|
136
|
+
def ruby_value_to_ora_value(value, type=nil)
|
137
|
+
type ||= value.class
|
138
|
+
case type.to_s.to_sym
|
139
|
+
when :Fixnum, :BigDecimal, :String
|
140
|
+
value
|
141
|
+
when :OraNumber
|
111
142
|
# pass parameters as OraNumber to avoid rounding errors
|
112
|
-
case
|
143
|
+
case value
|
113
144
|
when Bignum
|
114
|
-
OraNumber.new(
|
145
|
+
OraNumber.new(value.to_s)
|
115
146
|
when BigDecimal
|
116
|
-
OraNumber.new(
|
147
|
+
OraNumber.new(value.to_s('F'))
|
148
|
+
when TrueClass
|
149
|
+
OraNumber.new(1)
|
150
|
+
when FalseClass
|
151
|
+
OraNumber.new(0)
|
117
152
|
else
|
118
|
-
|
153
|
+
value
|
119
154
|
end
|
120
|
-
|
121
|
-
case
|
155
|
+
when :DateTime
|
156
|
+
case value
|
122
157
|
when Time
|
123
|
-
::DateTime.civil(
|
158
|
+
::DateTime.civil(value.year, value.month, value.day, value.hour, value.min, value.sec, Rational(value.utc_offset, 86400))
|
124
159
|
when DateTime
|
125
|
-
|
160
|
+
value
|
126
161
|
when Date
|
127
|
-
::DateTime.civil(
|
162
|
+
::DateTime.civil(value.year, value.month, value.day, 0, 0, 0, 0)
|
128
163
|
else
|
129
|
-
|
164
|
+
value
|
130
165
|
end
|
131
|
-
|
132
|
-
# ruby-oci8 cannot create CLOB from ''
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
val = nil if val == ''
|
138
|
-
OCI8::BLOB.new(raw_oci_connection, val)
|
166
|
+
when :"OCI8::CLOB", :"OCI8::BLOB"
|
167
|
+
# ruby-oci8 cannot create CLOB/BLOB from ''
|
168
|
+
value = nil if value == ''
|
169
|
+
type.new(raw_oci_connection, value)
|
170
|
+
when :"OCI8::Cursor"
|
171
|
+
value && value.raw_cursor
|
139
172
|
else
|
140
|
-
|
173
|
+
# collections and object types
|
174
|
+
if type.superclass == OCI8::Object::Base
|
175
|
+
return nil if value.nil?
|
176
|
+
tdo = raw_oci_connection.get_tdo_by_class(type)
|
177
|
+
if tdo.is_collection?
|
178
|
+
raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
|
179
|
+
elem_list = value.map do |elem|
|
180
|
+
if (attr_tdo = tdo.coll_attr.typeinfo)
|
181
|
+
attr_type, attr_length = plsql_to_ruby_data_type(:data_type => 'OBJECT', :sql_type_name => attr_tdo.typename)
|
182
|
+
else
|
183
|
+
attr_type = elem.class
|
184
|
+
end
|
185
|
+
ruby_value_to_ora_value(elem, attr_type)
|
186
|
+
end
|
187
|
+
# construct collection value
|
188
|
+
# TODO: change setting instance variable to appropriate ruby-oci8 method call when available
|
189
|
+
collection = type.new(raw_oci_connection)
|
190
|
+
collection.instance_variable_set('@attributes', elem_list)
|
191
|
+
collection
|
192
|
+
else # object type
|
193
|
+
raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
|
194
|
+
object_attrs = value.dup
|
195
|
+
object_attrs.keys.each do |key|
|
196
|
+
raise ArgumentError, "Wrong object type field passed to PL/SQL procedure" unless (attr = tdo.attr_getters[key])
|
197
|
+
case attr.datatype
|
198
|
+
when OCI8::TDO::ATTR_NAMED_TYPE, OCI8::TDO::ATTR_NAMED_COLLECTION
|
199
|
+
# nested object type or collection
|
200
|
+
attr_type, attr_length = plsql_to_ruby_data_type(:data_type => 'OBJECT', :sql_type_name => attr.typeinfo.typename)
|
201
|
+
object_attrs[key] = ruby_value_to_ora_value(object_attrs[key], attr_type)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
type.new(raw_oci_connection, object_attrs)
|
205
|
+
end
|
206
|
+
# all other cases
|
207
|
+
else
|
208
|
+
value
|
209
|
+
end
|
141
210
|
end
|
142
211
|
end
|
143
212
|
|
144
|
-
def ora_value_to_ruby_value(
|
145
|
-
case
|
146
|
-
when Float, OraNumber
|
147
|
-
ora_number_to_ruby_number(
|
213
|
+
def ora_value_to_ruby_value(value)
|
214
|
+
case value
|
215
|
+
when Float, OraNumber, BigDecimal
|
216
|
+
ora_number_to_ruby_number(value)
|
148
217
|
when DateTime, OraDate
|
149
|
-
ora_date_to_ruby_date(
|
218
|
+
ora_date_to_ruby_date(value)
|
150
219
|
when OCI8::LOB
|
151
|
-
if
|
152
|
-
|
153
|
-
|
220
|
+
if value.available?
|
221
|
+
value.rewind
|
222
|
+
value.read
|
154
223
|
else
|
155
224
|
nil
|
156
225
|
end
|
226
|
+
when OCI8::Object::Base
|
227
|
+
tdo = raw_oci_connection.get_tdo_by_class(value.class)
|
228
|
+
if tdo.is_collection?
|
229
|
+
value.to_ary.map{|e| ora_value_to_ruby_value(e)}
|
230
|
+
else # object type
|
231
|
+
tdo.attributes.inject({}) do |hash, attr|
|
232
|
+
hash[attr.name] = ora_value_to_ruby_value(value.instance_variable_get(:@attributes)[attr.name])
|
233
|
+
hash
|
234
|
+
end
|
235
|
+
end
|
236
|
+
when OCI8::Cursor
|
237
|
+
Cursor.new(self, value)
|
157
238
|
else
|
158
|
-
|
239
|
+
value
|
159
240
|
end
|
160
241
|
end
|
161
242
|
|
@@ -174,8 +255,7 @@ module PLSQL
|
|
174
255
|
|
175
256
|
def ora_number_to_ruby_number(num)
|
176
257
|
# return BigDecimal instead of Float to avoid rounding errors
|
177
|
-
|
178
|
-
num == (num_to_i = num.to_i) ? num_to_i : BigDecimal.new(num.to_s)
|
258
|
+
num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal.new(num.to_s))
|
179
259
|
end
|
180
260
|
|
181
261
|
def ora_date_to_ruby_date(val)
|
data/lib/plsql/package.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module PLSQL
|
2
2
|
|
3
|
-
module PackageClassMethods
|
3
|
+
module PackageClassMethods #:nodoc:
|
4
4
|
def find(schema, package)
|
5
5
|
if schema.select_first("
|
6
6
|
SELECT object_name FROM all_objects
|
@@ -27,7 +27,7 @@ module PLSQL
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
class Package
|
30
|
+
class Package #:nodoc:
|
31
31
|
extend PackageClassMethods
|
32
32
|
|
33
33
|
def initialize(schema, package, override_schema_name = nil)
|
@@ -39,12 +39,12 @@ module PLSQL
|
|
39
39
|
|
40
40
|
private
|
41
41
|
|
42
|
-
def method_missing(method, *args)
|
42
|
+
def method_missing(method, *args, &block)
|
43
43
|
if procedure = @procedures[method]
|
44
|
-
procedure.exec(*args)
|
44
|
+
procedure.exec(*args, &block)
|
45
45
|
elsif procedure = Procedure.find(@schema, method, @package, @override_schema_name)
|
46
46
|
@procedures[method] = procedure
|
47
|
-
procedure.exec(*args)
|
47
|
+
procedure.exec(*args, &block)
|
48
48
|
else
|
49
49
|
raise ArgumentError, "No PL/SQL procedure found"
|
50
50
|
end
|
data/lib/plsql/procedure.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module PLSQL
|
2
2
|
|
3
|
-
module ProcedureClassMethods
|
3
|
+
module ProcedureClassMethods #:nodoc:
|
4
4
|
def find(schema, procedure, package = nil, override_schema_name = nil)
|
5
5
|
if package.nil?
|
6
6
|
if schema.select_first("
|
@@ -38,9 +38,12 @@ module PLSQL
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
class Procedure
|
41
|
+
class Procedure #:nodoc:
|
42
42
|
extend ProcedureClassMethods
|
43
43
|
|
44
|
+
attr_reader :arguments, :argument_list, :out_list, :return
|
45
|
+
attr_reader :schema, :schema_name, :package, :procedure
|
46
|
+
|
44
47
|
def initialize(schema, procedure, package = nil, override_schema_name = nil)
|
45
48
|
@schema = schema
|
46
49
|
@schema_name = override_schema_name || schema.schema_name
|
@@ -51,6 +54,10 @@ module PLSQL
|
|
51
54
|
@out_list = {}
|
52
55
|
@return = {}
|
53
56
|
@overloaded = false
|
57
|
+
|
58
|
+
# store reference to previous level record or collection metadata
|
59
|
+
previous_level_argument_metadata = {}
|
60
|
+
|
54
61
|
# RSI: due to 10gR2 all_arguments performance issue SELECT split into two statements
|
55
62
|
# added condition to ensure that if object is package then package specification not body is selected
|
56
63
|
object_id = @schema.connection.select_first("
|
@@ -62,41 +69,68 @@ module PLSQL
|
|
62
69
|
", @schema_name, @package ? @package : @procedure
|
63
70
|
)[0] rescue nil
|
64
71
|
num_rows = @schema.connection.select_all("
|
65
|
-
SELECT
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
AND
|
72
|
+
SELECT overload, argument_name, position, data_level,
|
73
|
+
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
74
|
+
type_owner, type_name, type_subname
|
75
|
+
FROM all_arguments
|
76
|
+
WHERE object_id = :object_id
|
77
|
+
AND owner = :owner
|
78
|
+
AND object_name = :procedure_name
|
79
|
+
AND NVL(package_name,'nil') = :package
|
80
|
+
ORDER BY overload, sequence
|
71
81
|
", object_id, @schema_name, @procedure, @package ? @package : 'nil'
|
72
82
|
) do |r|
|
73
83
|
|
74
|
-
argument_name, position,
|
84
|
+
overload, argument_name, position, data_level,
|
85
|
+
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
86
|
+
type_owner, type_name, type_subname = r
|
75
87
|
|
76
88
|
@overloaded ||= !overload.nil?
|
77
89
|
# if not overloaded then store arguments at key 0
|
78
90
|
overload ||= 0
|
79
91
|
@arguments[overload] ||= {}
|
80
92
|
@return[overload] ||= nil
|
81
|
-
|
93
|
+
|
94
|
+
raise ArgumentError, "Parameter type definition inside package is not supported, use CREATE TYPE outside package" if type_subname
|
95
|
+
|
96
|
+
argument_metadata = {
|
97
|
+
:position => position && position.to_i,
|
98
|
+
:data_type => data_type,
|
99
|
+
:in_out => in_out,
|
100
|
+
:data_length => data_length && data_length.to_i,
|
101
|
+
:data_precision => data_precision && data_precision.to_i,
|
102
|
+
:data_scale => data_scale && data_scale.to_i,
|
103
|
+
:char_used => char_used,
|
104
|
+
:type_owner => type_owner,
|
105
|
+
:type_name => type_name,
|
106
|
+
:type_subname => type_subname,
|
107
|
+
:sql_type_name => "#{type_owner}.#{type_name}"
|
108
|
+
}
|
109
|
+
if composite_type?(data_type)
|
110
|
+
case data_type
|
111
|
+
when 'PL/SQL RECORD'
|
112
|
+
argument_metadata[:fields] = {}
|
113
|
+
end
|
114
|
+
previous_level_argument_metadata[data_level] = argument_metadata
|
115
|
+
end
|
116
|
+
|
117
|
+
# if parameter
|
82
118
|
if argument_name
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
119
|
+
# top level parameter
|
120
|
+
if data_level == 0
|
121
|
+
@arguments[overload][argument_name.downcase.to_sym] = argument_metadata
|
122
|
+
# or lower level part of composite type
|
123
|
+
else
|
124
|
+
case previous_level_argument_metadata[data_level - 1][:data_type]
|
125
|
+
when 'PL/SQL RECORD'
|
126
|
+
previous_level_argument_metadata[data_level - 1][:fields][argument_name.downcase.to_sym] = argument_metadata
|
127
|
+
when 'TABLE', 'VARRAY'
|
128
|
+
previous_level_argument_metadata[data_level - 1][:element] = argument_metadata
|
129
|
+
end
|
130
|
+
end
|
91
131
|
# if function has return value
|
92
|
-
elsif
|
93
|
-
@return[overload] =
|
94
|
-
:data_type => data_type,
|
95
|
-
:in_out => in_out,
|
96
|
-
:data_length => data_length,
|
97
|
-
:data_precision => data_precision,
|
98
|
-
:data_scale => data_scale
|
99
|
-
}
|
132
|
+
elsif argument_name.nil? && data_level == 0 && in_out == 'OUT'
|
133
|
+
@return[overload] = argument_metadata
|
100
134
|
end
|
101
135
|
end
|
102
136
|
# if procedure is without arguments then create default empty argument list for default overload
|
@@ -108,118 +142,19 @@ module PLSQL
|
|
108
142
|
@out_list[overload] = @argument_list[overload].select {|k| @arguments[overload][k][:in_out] =~ /OUT/}
|
109
143
|
end
|
110
144
|
end
|
111
|
-
|
145
|
+
|
146
|
+
PLSQL_COMPOSITE_TYPES = ['PL/SQL RECORD', 'TABLE', 'VARRAY'].freeze
|
147
|
+
def composite_type?(data_type)
|
148
|
+
PLSQL_COMPOSITE_TYPES.include? data_type
|
149
|
+
end
|
150
|
+
|
112
151
|
def overloaded?
|
113
152
|
@overloaded
|
114
153
|
end
|
115
154
|
|
116
|
-
def exec(*args)
|
117
|
-
|
118
|
-
|
119
|
-
if @overloaded
|
120
|
-
# named arguments
|
121
|
-
if args.size == 1 && args[0].is_a?(Hash)
|
122
|
-
number_of_args = args[0].keys.size
|
123
|
-
overload = @argument_list.keys.detect do |ov|
|
124
|
-
@argument_list[ov].size == number_of_args &&
|
125
|
-
@arguments[ov].keys.sort_by{|k| k.to_s} == args[0].keys.sort_by{|k| k.to_s}
|
126
|
-
end
|
127
|
-
# sequential arguments
|
128
|
-
# TODO: should try to implement matching by types of arguments
|
129
|
-
else
|
130
|
-
number_of_args = args.size
|
131
|
-
overload = @argument_list.keys.detect do |ov|
|
132
|
-
@argument_list[ov].size == number_of_args
|
133
|
-
end
|
134
|
-
end
|
135
|
-
raise ArgumentError, "Wrong number of arguments passed to overloaded PL/SQL procedure" unless overload
|
136
|
-
else
|
137
|
-
overload = 0
|
138
|
-
end
|
139
|
-
|
140
|
-
sql = "BEGIN\n"
|
141
|
-
sql << ":return := " if @return[overload]
|
142
|
-
sql << "#{@schema_name}." if @schema_name
|
143
|
-
sql << "#{@package}." if @package
|
144
|
-
sql << "#{@procedure}("
|
145
|
-
|
146
|
-
# Named arguments
|
147
|
-
args_list = []
|
148
|
-
args_hash = {}
|
149
|
-
if args.size == 1 and args[0].is_a?(Hash)
|
150
|
-
sql << args[0].map do |k,v|
|
151
|
-
raise ArgumentError, "Wrong argument passed to PL/SQL procedure" unless @arguments[overload][k]
|
152
|
-
args_list << k
|
153
|
-
args_hash[k] = v
|
154
|
-
"#{k.to_s} => :#{k.to_s}"
|
155
|
-
end.join(', ')
|
156
|
-
# Sequential arguments
|
157
|
-
else
|
158
|
-
raise ArgumentError, "Too many arguments passed to PL/SQL procedure" if args.size > @argument_list[overload].size
|
159
|
-
# Add missing arguments with nil value
|
160
|
-
args = args + [nil]*(@argument_list[overload].size-args.size) if args.size < @argument_list[overload].size
|
161
|
-
i = 0
|
162
|
-
sql << args.map do |v|
|
163
|
-
k = @argument_list[overload][i]
|
164
|
-
i += 1
|
165
|
-
args_list << k
|
166
|
-
args_hash[k] = v
|
167
|
-
":#{k.to_s}"
|
168
|
-
end.join(', ')
|
169
|
-
end
|
170
|
-
sql << ");\n"
|
171
|
-
sql << "END;\n"
|
172
|
-
|
173
|
-
cursor = @schema.connection.parse(sql)
|
174
|
-
|
175
|
-
args_list.each do |k|
|
176
|
-
data_type, data_length = plsql_to_ruby_data_type(@arguments[overload][k])
|
177
|
-
cursor.bind_param(":#{k.to_s}", ruby_value_to_ora_value(args_hash[k], data_type),
|
178
|
-
data_type, data_length, @arguments[overload][k][:in_out])
|
179
|
-
end
|
180
|
-
|
181
|
-
if @return[overload]
|
182
|
-
data_type, data_length = plsql_to_ruby_data_type(@return[overload])
|
183
|
-
cursor.bind_param(":return", nil, data_type, data_length, 'OUT')
|
184
|
-
end
|
185
|
-
|
186
|
-
cursor.exec
|
187
|
-
|
188
|
-
# if function with output parameters
|
189
|
-
if @return[overload] && @out_list[overload].size > 0
|
190
|
-
result = [ora_value_to_ruby_value(cursor[':return']), {}]
|
191
|
-
@out_list[overload].each do |k|
|
192
|
-
result[1][k] = ora_value_to_ruby_value(cursor[":#{k}"])
|
193
|
-
end
|
194
|
-
# if function without output parameters
|
195
|
-
elsif @return[overload]
|
196
|
-
result = ora_value_to_ruby_value(cursor[':return'])
|
197
|
-
# if procedure with output parameters
|
198
|
-
elsif @out_list[overload].size > 0
|
199
|
-
result = {}
|
200
|
-
@out_list[overload].each do |k|
|
201
|
-
result[k] = ora_value_to_ruby_value(cursor[":#{k}"])
|
202
|
-
end
|
203
|
-
# if procedure without output parameters
|
204
|
-
else
|
205
|
-
result = nil
|
206
|
-
end
|
207
|
-
cursor.close
|
208
|
-
result
|
209
|
-
end
|
210
|
-
|
211
|
-
private
|
212
|
-
|
213
|
-
def plsql_to_ruby_data_type(argument)
|
214
|
-
@schema.connection.plsql_to_ruby_data_type(argument[:data_type],argument[:data_length])
|
215
|
-
end
|
216
|
-
|
217
|
-
def ruby_value_to_ora_value(val, type)
|
218
|
-
@schema.connection.ruby_value_to_ora_value(val, type)
|
219
|
-
end
|
220
|
-
|
221
|
-
def ora_value_to_ruby_value(val)
|
222
|
-
@schema.connection.ora_value_to_ruby_value(val)
|
155
|
+
def exec(*args, &block)
|
156
|
+
call = ProcedureCall.new(self, args)
|
157
|
+
call.exec(&block)
|
223
158
|
end
|
224
159
|
|
225
160
|
end
|