ruby-plsql 0.5.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/stale.yml +37 -0
- data/.github/workflows/rubocop.yml +37 -0
- data/.github/workflows/test.yml +69 -0
- data/.rubocop.yml +147 -0
- data/.travis.yml +88 -0
- data/.travis/oracle/download.sh +15 -0
- data/.travis/oracle/install.sh +32 -0
- data/.travis/setup_accounts.sh +9 -0
- data/Gemfile +17 -9
- data/History.txt +76 -0
- data/README.md +29 -6
- data/Rakefile +31 -26
- data/VERSION +1 -1
- data/Vagrantfile +4 -4
- data/ci/network/admin/tnsnames.ora +7 -0
- data/ci/setup_accounts.sh +9 -0
- data/gemfiles/Gemfile.activerecord-5.0 +21 -0
- data/gemfiles/Gemfile.activerecord-5.1 +21 -0
- data/gemfiles/Gemfile.activerecord-5.2 +21 -0
- data/gemfiles/Gemfile.activerecord-6.0 +21 -0
- data/gemfiles/Gemfile.activerecord-6.1 +21 -0
- data/gemfiles/Gemfile.activerecord-main +21 -0
- data/lib/plsql/connection.rb +19 -22
- data/lib/plsql/helpers.rb +1 -3
- data/lib/plsql/jdbc_connection.rb +70 -68
- data/lib/plsql/oci8_patches.rb +2 -2
- data/lib/plsql/oci_connection.rb +62 -77
- data/lib/plsql/package.rb +61 -46
- data/lib/plsql/procedure.rb +358 -78
- data/lib/plsql/procedure_call.rb +508 -463
- data/lib/plsql/schema.rb +96 -101
- data/lib/plsql/sequence.rb +10 -13
- data/lib/plsql/sql_statements.rb +9 -11
- data/lib/plsql/table.rb +60 -63
- data/lib/plsql/type.rb +71 -76
- data/lib/plsql/variable.rb +90 -94
- data/lib/plsql/version.rb +1 -1
- data/lib/plsql/view.rb +16 -19
- data/ruby-plsql.gemspec +55 -35
- data/spec/plsql/connection_spec.rb +72 -66
- data/spec/plsql/package_spec.rb +63 -14
- data/spec/plsql/procedure_spec.rb +603 -261
- data/spec/plsql/schema_spec.rb +47 -23
- data/spec/plsql/sequence_spec.rb +2 -2
- data/spec/plsql/sql_statements_spec.rb +6 -6
- data/spec/plsql/table_spec.rb +84 -79
- data/spec/plsql/type_spec.rb +24 -30
- data/spec/plsql/variable_spec.rb +80 -88
- data/spec/plsql/version_spec.rb +4 -4
- data/spec/plsql/view_spec.rb +42 -42
- data/spec/spec_helper.rb +38 -35
- data/spec/support/create_arunit_user.sql +2 -0
- data/spec/support/custom_config.rb.sample +14 -0
- data/spec/support/test_db.rb +12 -13
- data/spec/support/unlock_and_setup_hr_user.sql +2 -0
- metadata +111 -34
data/lib/plsql/oci_connection.rb
CHANGED
@@ -18,17 +18,16 @@ require "plsql/oci8_patches"
|
|
18
18
|
|
19
19
|
# check ruby-oci8 version
|
20
20
|
required_oci8_version = [2, 0, 3]
|
21
|
-
oci8_version_ints = OCI8::VERSION.scan(/\d+/).map{|s| s.to_i}
|
21
|
+
oci8_version_ints = OCI8::VERSION.scan(/\d+/).map { |s| s.to_i }
|
22
22
|
if (oci8_version_ints <=> required_oci8_version) < 0
|
23
23
|
raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
|
24
24
|
end
|
25
25
|
|
26
26
|
module PLSQL
|
27
27
|
class OCIConnection < Connection #:nodoc:
|
28
|
-
|
29
28
|
def self.create_raw(params)
|
30
29
|
connection_string = if params[:host]
|
31
|
-
"//#{params[:host]}:#{params[:port]||1521}/#{params[:database]}"
|
30
|
+
"//#{params[:host]}:#{params[:port] || 1521}/#{params[:database]}"
|
32
31
|
else
|
33
32
|
params[:database]
|
34
33
|
end
|
@@ -47,7 +46,7 @@ module PLSQL
|
|
47
46
|
def rollback
|
48
47
|
raw_connection.rollback
|
49
48
|
end
|
50
|
-
|
49
|
+
|
51
50
|
def autocommit?
|
52
51
|
raw_connection.autocommit?
|
53
52
|
end
|
@@ -68,14 +67,17 @@ module PLSQL
|
|
68
67
|
class Cursor #:nodoc:
|
69
68
|
include Connection::CursorCommon
|
70
69
|
|
71
|
-
# stack of open cursors
|
72
|
-
@@open_cursors = []
|
73
70
|
attr_reader :raw_cursor
|
74
71
|
|
72
|
+
# stack of open cursors per thread
|
73
|
+
def self.open_cursors
|
74
|
+
Thread.current[:plsql_oci_cursor_stack] ||= []
|
75
|
+
end
|
76
|
+
|
75
77
|
def initialize(conn, raw_cursor)
|
76
78
|
@connection = conn
|
77
79
|
@raw_cursor = raw_cursor
|
78
|
-
|
80
|
+
self.class.open_cursors.push self
|
79
81
|
end
|
80
82
|
|
81
83
|
def self.new_from_parse(conn, sql)
|
@@ -83,7 +85,7 @@ module PLSQL
|
|
83
85
|
self.new(conn, raw_cursor)
|
84
86
|
end
|
85
87
|
|
86
|
-
def self.new_from_query(conn, sql, bindvars=[], options={})
|
88
|
+
def self.new_from_query(conn, sql, bindvars = [], options = {})
|
87
89
|
cursor = new_from_parse(conn, sql)
|
88
90
|
if prefetch_rows = options[:prefetch_rows]
|
89
91
|
cursor.prefetch_rows = prefetch_rows
|
@@ -101,7 +103,7 @@ module PLSQL
|
|
101
103
|
ora_value = @connection.ruby_value_to_ora_value(value, type)
|
102
104
|
@raw_cursor.bind_param(arg, ora_value, type, length)
|
103
105
|
end
|
104
|
-
|
106
|
+
|
105
107
|
def exec(*bindvars)
|
106
108
|
@raw_cursor.exec(*bindvars)
|
107
109
|
end
|
@@ -112,11 +114,11 @@ module PLSQL
|
|
112
114
|
|
113
115
|
def fetch
|
114
116
|
row = @raw_cursor.fetch
|
115
|
-
row && row.map{|v| @connection.ora_value_to_ruby_value(v)}
|
117
|
+
row && row.map { |v| @connection.ora_value_to_ruby_value(v) }
|
116
118
|
end
|
117
119
|
|
118
120
|
def fields
|
119
|
-
@fields ||= @raw_cursor.get_col_names.map{|c| c.downcase.to_sym}
|
121
|
+
@fields ||= @raw_cursor.get_col_names.map { |c| c.downcase.to_sym }
|
120
122
|
end
|
121
123
|
|
122
124
|
def close_raw_cursor
|
@@ -125,38 +127,37 @@ module PLSQL
|
|
125
127
|
|
126
128
|
def close
|
127
129
|
# close all cursors that were created after this one
|
128
|
-
while (open_cursor =
|
130
|
+
while (open_cursor = self.class.open_cursors.pop) && !open_cursor.equal?(self)
|
129
131
|
open_cursor.close_raw_cursor
|
130
132
|
end
|
131
133
|
close_raw_cursor
|
132
134
|
end
|
133
|
-
|
134
135
|
end
|
135
136
|
|
136
137
|
def parse(sql)
|
137
138
|
Cursor.new_from_parse(self, sql)
|
138
139
|
end
|
139
140
|
|
140
|
-
def cursor_from_query(sql, bindvars=[], options={})
|
141
|
+
def cursor_from_query(sql, bindvars = [], options = {})
|
141
142
|
Cursor.new_from_query(self, sql, bindvars, options)
|
142
143
|
end
|
143
144
|
|
144
145
|
def plsql_to_ruby_data_type(metadata)
|
145
146
|
data_type, data_length = metadata[:data_type], metadata[:data_length]
|
146
147
|
case data_type
|
147
|
-
when "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
|
148
|
+
when "VARCHAR", "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
|
148
149
|
[String, data_length || 32767]
|
149
150
|
when "CLOB", "NCLOB"
|
150
151
|
[OCI8::CLOB, nil]
|
151
152
|
when "BLOB"
|
152
153
|
[OCI8::BLOB, nil]
|
153
|
-
when "NUMBER", "PLS_INTEGER", "BINARY_INTEGER"
|
154
|
+
when "NUMBER", "NATURAL", "NATURALN", "POSITIVE", "POSITIVEN", "SIGNTYPE", "SIMPLE_INTEGER", "PLS_INTEGER", "BINARY_INTEGER"
|
154
155
|
[OraNumber, nil]
|
155
156
|
when "DATE"
|
156
157
|
[DateTime, nil]
|
157
158
|
when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
|
158
159
|
[Time, nil]
|
159
|
-
when "TABLE", "VARRAY", "OBJECT"
|
160
|
+
when "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
|
160
161
|
# create Ruby class for collection
|
161
162
|
klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
|
162
163
|
unless klass
|
@@ -171,18 +172,16 @@ module PLSQL
|
|
171
172
|
end
|
172
173
|
end
|
173
174
|
|
174
|
-
def ruby_value_to_ora_value(value, type=nil)
|
175
|
+
def ruby_value_to_ora_value(value, type = nil)
|
175
176
|
type ||= value.class
|
176
177
|
case type.to_s.to_sym
|
177
|
-
when :
|
178
|
+
when :Integer, :BigDecimal, :String
|
178
179
|
value
|
179
180
|
when :OraNumber
|
180
181
|
# pass parameters as OraNumber to avoid rounding errors
|
181
182
|
case value
|
182
|
-
when Bignum
|
183
|
-
OraNumber.new(value.to_s)
|
184
183
|
when BigDecimal
|
185
|
-
OraNumber.new(value.to_s(
|
184
|
+
OraNumber.new(value.to_s("F"))
|
186
185
|
when TrueClass
|
187
186
|
OraNumber.new(1)
|
188
187
|
when FalseClass
|
@@ -215,7 +214,7 @@ module PLSQL
|
|
215
214
|
raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
|
216
215
|
elem_list = value.map do |elem|
|
217
216
|
if (attr_tdo = tdo.coll_attr.typeinfo)
|
218
|
-
attr_type,
|
217
|
+
attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr_tdo.typename)
|
219
218
|
else
|
220
219
|
attr_type = elem.class
|
221
220
|
end
|
@@ -224,7 +223,7 @@ module PLSQL
|
|
224
223
|
# construct collection value
|
225
224
|
# TODO: change setting instance variable to appropriate ruby-oci8 method call when available
|
226
225
|
collection = type.new(raw_oci_connection)
|
227
|
-
collection.instance_variable_set(
|
226
|
+
collection.instance_variable_set("@attributes", elem_list)
|
228
227
|
collection
|
229
228
|
else # object type
|
230
229
|
raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
|
@@ -234,7 +233,7 @@ module PLSQL
|
|
234
233
|
case attr.datatype
|
235
234
|
when OCI8::TDO::ATTR_NAMED_TYPE, OCI8::TDO::ATTR_NAMED_COLLECTION
|
236
235
|
# nested object type or collection
|
237
|
-
attr_type,
|
236
|
+
attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr.typeinfo.typename)
|
238
237
|
object_attrs[key] = ruby_value_to_ora_value(object_attrs[key], attr_type)
|
239
238
|
end
|
240
239
|
end
|
@@ -263,7 +262,7 @@ module PLSQL
|
|
263
262
|
when OCI8::Object::Base
|
264
263
|
tdo = raw_oci_connection.get_tdo_by_class(value.class)
|
265
264
|
if tdo.is_collection?
|
266
|
-
value.to_ary.map{|e| ora_value_to_ruby_value(e)}
|
265
|
+
value.to_ary.map { |e| ora_value_to_ruby_value(e) }
|
267
266
|
else # object type
|
268
267
|
tdo.attributes.inject({}) do |hash, attr|
|
269
268
|
hash[attr.name] = ora_value_to_ruby_value(value.instance_variable_get(:@attributes)[attr.name])
|
@@ -277,63 +276,49 @@ module PLSQL
|
|
277
276
|
end
|
278
277
|
end
|
279
278
|
|
280
|
-
def describe_synonym(schema_name, synonym_name)
|
281
|
-
if schema_name == 'PUBLIC'
|
282
|
-
full_name = synonym_name.to_s
|
283
|
-
else
|
284
|
-
full_name = "#{schema_name}.#{synonym_name}"
|
285
|
-
end
|
286
|
-
metadata = raw_connection.describe_synonym(full_name)
|
287
|
-
[metadata.schema_name, metadata.name]
|
288
|
-
rescue OCIError
|
289
|
-
nil
|
290
|
-
end
|
291
|
-
|
292
279
|
def database_version
|
293
|
-
@database_version ||= (version = raw_connection.oracle_server_version) &&
|
280
|
+
@database_version ||= (version = raw_connection.oracle_server_version) &&
|
294
281
|
[version.major, version.minor, version.update, version.patch]
|
295
282
|
end
|
296
283
|
|
297
284
|
private
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
def ora_number_to_ruby_number(num)
|
310
|
-
# return BigDecimal instead of Float to avoid rounding errors
|
311
|
-
num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal.new(num.to_s))
|
312
|
-
end
|
313
|
-
|
314
|
-
def ora_date_to_ruby_date(val)
|
315
|
-
case val
|
316
|
-
when DateTime
|
317
|
-
# similar implementation as in oracle_enhanced adapter
|
318
|
-
begin
|
319
|
-
Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.min, val.sec)
|
320
|
-
rescue
|
321
|
-
offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
|
322
|
-
DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, offset)
|
323
|
-
end
|
324
|
-
when OraDate
|
325
|
-
# similar implementation as in oracle_enhanced adapter
|
326
|
-
begin
|
327
|
-
Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.minute, val.second)
|
328
|
-
rescue
|
329
|
-
offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
|
330
|
-
DateTime.civil(val.year, val.month, val.day, val.hour, val.minute, val.second, offset)
|
285
|
+
|
286
|
+
def raw_oci_connection
|
287
|
+
if raw_connection.is_a? OCI8
|
288
|
+
raw_connection
|
289
|
+
# ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
|
290
|
+
# in this case we need to pass original OCI8 connection
|
291
|
+
else
|
292
|
+
raw_connection.instance_variable_get(:@connection)
|
331
293
|
end
|
332
|
-
else
|
333
|
-
val
|
334
294
|
end
|
335
|
-
end
|
336
295
|
|
296
|
+
def ora_number_to_ruby_number(num)
|
297
|
+
# return BigDecimal instead of Float to avoid rounding errors
|
298
|
+
num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal(num.to_s))
|
299
|
+
end
|
300
|
+
|
301
|
+
def ora_date_to_ruby_date(val)
|
302
|
+
case val
|
303
|
+
when DateTime
|
304
|
+
# similar implementation as in oracle_enhanced adapter
|
305
|
+
begin
|
306
|
+
Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.min, val.sec)
|
307
|
+
rescue
|
308
|
+
offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
|
309
|
+
DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, offset)
|
310
|
+
end
|
311
|
+
when OraDate
|
312
|
+
# similar implementation as in oracle_enhanced adapter
|
313
|
+
begin
|
314
|
+
Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.minute, val.second)
|
315
|
+
rescue
|
316
|
+
offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
|
317
|
+
DateTime.civil(val.year, val.month, val.day, val.hour, val.minute, val.second, offset)
|
318
|
+
end
|
319
|
+
else
|
320
|
+
val
|
321
|
+
end
|
322
|
+
end
|
337
323
|
end
|
338
|
-
|
339
|
-
end
|
324
|
+
end
|
data/lib/plsql/package.rb
CHANGED
@@ -1,29 +1,34 @@
|
|
1
1
|
module PLSQL
|
2
|
-
|
3
2
|
module PackageClassMethods #:nodoc:
|
4
3
|
def find(schema, package)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
4
|
+
package_name = package.to_s.upcase
|
5
|
+
find_in_schema(schema, package_name) || find_by_synonym(schema, package_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_in_schema(schema, package_name)
|
9
|
+
row = schema.select_first(<<-SQL, schema.schema_name, package_name)
|
10
|
+
SELECT object_name
|
11
|
+
FROM all_objects
|
12
|
+
WHERE owner = :owner
|
13
|
+
AND object_name = :package
|
14
|
+
AND object_type = 'PACKAGE'
|
15
|
+
SQL
|
16
|
+
new(schema, package_name) if row
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_by_synonym(schema, package_name)
|
20
|
+
row = schema.select_first(<<-SQL, schema.schema_name, package_name)
|
21
|
+
SELECT o.object_name, o.owner
|
22
|
+
FROM all_synonyms s,
|
23
|
+
all_objects o
|
24
|
+
WHERE s.owner IN (:owner, 'PUBLIC')
|
25
|
+
AND s.synonym_name = :synonym_name
|
26
|
+
AND o.owner = s.table_owner
|
27
|
+
AND o.object_name = s.table_name
|
28
|
+
AND o.object_type = 'PACKAGE'
|
29
|
+
ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)
|
30
|
+
SQL
|
31
|
+
new(schema, row[0], row[1]) if row
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
@@ -41,32 +46,42 @@ module PLSQL
|
|
41
46
|
PLSQL::Procedure.find(@schema, name, @package) ? true : false
|
42
47
|
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
method = method.to_s.chop.to_sym
|
49
|
+
def [](object_name)
|
50
|
+
object_name = object_name.to_s.downcase
|
51
|
+
@package_objects[object_name] ||= [Procedure, Variable].inject(nil) do |res, object_type|
|
52
|
+
res || object_type.find(@schema, object_name, @package, @override_schema_name)
|
49
53
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def method_missing(method, *args, &block)
|
59
|
+
method = method.to_s
|
60
|
+
method.chop! if (assignment = method[/=$/])
|
61
|
+
|
62
|
+
case (object = self[method])
|
63
|
+
when Procedure
|
64
|
+
if assignment
|
65
|
+
raise ArgumentError, "Cannot assign value to package procedure '#{method.upcase}'"
|
66
|
+
end
|
67
|
+
object.exec(*args, &block)
|
68
|
+
when Variable
|
69
|
+
if assignment
|
70
|
+
unless args.size == 1 && block.nil?
|
71
|
+
raise ArgumentError, "Just one value can be assigned " \
|
72
|
+
"to package variable '#{method.upcase}'"
|
73
|
+
end
|
74
|
+
object.value = args[0]
|
75
|
+
else
|
76
|
+
unless args.size == 0 && block.nil?
|
77
|
+
raise ArgumentError, "Cannot pass arguments when getting " \
|
78
|
+
"package variable '#{method.upcase}' value"
|
79
|
+
end
|
80
|
+
object.value
|
81
|
+
end
|
61
82
|
else
|
62
|
-
raise ArgumentError, "
|
63
|
-
object.value
|
83
|
+
raise ArgumentError, "No PL/SQL procedure or variable '#{method.upcase}' found"
|
64
84
|
end
|
65
|
-
else
|
66
|
-
raise ArgumentError, "No PL/SQL procedure or variable '#{method.to_s.upcase}' found"
|
67
85
|
end
|
68
|
-
end
|
69
|
-
|
70
86
|
end
|
71
|
-
|
72
87
|
end
|
data/lib/plsql/procedure.rb
CHANGED
@@ -1,45 +1,44 @@
|
|
1
1
|
module PLSQL
|
2
|
-
|
3
2
|
module ProcedureClassMethods #:nodoc:
|
4
3
|
def find(schema, procedure, package = nil, override_schema_name = nil)
|
5
4
|
if package.nil?
|
6
5
|
if (row = schema.select_first(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
"SELECT #{procedure_object_id_src(schema)}.object_id
|
7
|
+
FROM all_procedures p, all_objects o
|
8
|
+
WHERE p.owner = :owner
|
9
|
+
AND p.object_name = :object_name
|
10
|
+
AND o.owner = p.owner
|
11
|
+
AND o.object_name = p.object_name
|
12
|
+
AND o.object_type in ('PROCEDURE', 'FUNCTION')",
|
14
13
|
schema.schema_name, procedure.to_s.upcase))
|
15
14
|
new(schema, procedure, nil, nil, row[0])
|
16
15
|
# search for synonym
|
17
16
|
elsif (row = schema.select_first(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
"SELECT o.owner, o.object_name, #{procedure_object_id_src(schema)}.object_id
|
18
|
+
FROM all_synonyms s, all_objects o, all_procedures p
|
19
|
+
WHERE s.owner IN (:owner, 'PUBLIC')
|
20
|
+
AND s.synonym_name = :synonym_name
|
21
|
+
AND o.owner = s.table_owner
|
22
|
+
AND o.object_name = s.table_name
|
23
|
+
AND o.object_type IN ('PROCEDURE','FUNCTION')
|
24
|
+
AND o.owner = p.owner
|
25
|
+
AND o.object_name = p.object_name
|
26
|
+
ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
|
28
27
|
schema.schema_name, procedure.to_s.upcase))
|
29
28
|
new(schema, row[1], nil, row[0], row[2])
|
30
29
|
else
|
31
30
|
nil
|
32
31
|
end
|
33
32
|
elsif package && (row = schema.select_first(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
# older Oracle versions do not have object_id column in all_procedures
|
34
|
+
"SELECT #{procedure_object_id_src(schema)}.object_id
|
35
|
+
FROM all_procedures p, all_objects o
|
36
|
+
WHERE p.owner = :owner
|
37
|
+
AND p.object_name = :object_name
|
38
|
+
AND p.procedure_name = :procedure_name
|
39
|
+
AND o.owner = p.owner
|
40
|
+
AND o.object_name = p.object_name
|
41
|
+
AND o.object_type = 'PACKAGE'",
|
43
42
|
override_schema_name || schema.schema_name, package, procedure.to_s.upcase))
|
44
43
|
new(schema, procedure, package, override_schema_name, row[0])
|
45
44
|
else
|
@@ -49,9 +48,9 @@ module PLSQL
|
|
49
48
|
|
50
49
|
private
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
def procedure_object_id_src(schema)
|
52
|
+
(schema.connection.database_version <=> [11, 1, 0, 0]) >= 0 ? "p" : "o"
|
53
|
+
end
|
55
54
|
end
|
56
55
|
|
57
56
|
module ProcedureCommon #:nodoc:
|
@@ -61,21 +60,21 @@ module PLSQL
|
|
61
60
|
# return type string from metadata that can be used in DECLARE block or table definition
|
62
61
|
def self.type_to_sql(metadata) #:nodoc:
|
63
62
|
case metadata[:data_type]
|
64
|
-
when
|
63
|
+
when "NUMBER"
|
65
64
|
precision, scale = metadata[:data_precision], metadata[:data_scale]
|
66
|
-
"NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}": ""})" : ""}"
|
67
|
-
when
|
65
|
+
"NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}" : ""})" : ""}"
|
66
|
+
when "VARCHAR", "VARCHAR2", "CHAR"
|
68
67
|
length = case metadata[:char_used]
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
when "C" then "#{metadata[:char_length]} CHAR"
|
69
|
+
when "B" then "#{metadata[:data_length]} BYTE"
|
70
|
+
else
|
71
|
+
metadata[:data_length]
|
73
72
|
end
|
74
73
|
"#{metadata[:data_type]}#{length && "(#{length})"}"
|
75
|
-
when
|
74
|
+
when "NVARCHAR2", "NCHAR"
|
76
75
|
length = metadata[:char_length]
|
77
76
|
"#{metadata[:data_type]}#{length && "(#{length})"}"
|
78
|
-
when
|
77
|
+
when "PL/SQL TABLE", "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
|
79
78
|
metadata[:sql_type_name]
|
80
79
|
else
|
81
80
|
metadata[:data_type]
|
@@ -84,6 +83,14 @@ module PLSQL
|
|
84
83
|
|
85
84
|
# get procedure argument metadata from data dictionary
|
86
85
|
def get_argument_metadata #:nodoc:
|
86
|
+
if (@schema.connection.database_version <=> [18, 0, 0, 0]) >= 0
|
87
|
+
get_argument_metadata_from_18c
|
88
|
+
else
|
89
|
+
get_argument_metadata_below_18c
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_argument_metadata_below_18c #:nodoc:
|
87
94
|
@arguments = {}
|
88
95
|
@argument_list = {}
|
89
96
|
@out_list = {}
|
@@ -99,12 +106,14 @@ module PLSQL
|
|
99
106
|
@tmp_tables_created = {}
|
100
107
|
|
101
108
|
# subprogram_id column is available just from version 10g
|
102
|
-
subprogram_id_column = (@schema.connection.database_version <=> [10, 2, 0, 2]) >= 0 ?
|
109
|
+
subprogram_id_column = (@schema.connection.database_version <=> [10, 2, 0, 2]) >= 0 ? "subprogram_id" : "NULL"
|
110
|
+
# defaulted is available just from version 11g
|
111
|
+
defaulted_column = (@schema.connection.database_version <=> [11, 0, 0, 0]) >= 0 ? "defaulted" : "NULL"
|
103
112
|
|
104
113
|
@schema.select_all(
|
105
114
|
"SELECT #{subprogram_id_column}, object_name, TO_NUMBER(overload), argument_name, position, data_level,
|
106
115
|
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
107
|
-
char_length, type_owner, type_name, type_subname
|
116
|
+
char_length, type_owner, type_name, type_subname, #{defaulted_column}
|
108
117
|
FROM all_arguments
|
109
118
|
WHERE object_id = :object_id
|
110
119
|
AND owner = :owner
|
@@ -115,7 +124,7 @@ module PLSQL
|
|
115
124
|
|
116
125
|
subprogram_id, object_name, overload, argument_name, position, data_level,
|
117
126
|
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
118
|
-
char_length, type_owner, type_name, type_subname = r
|
127
|
+
char_length, type_owner, type_name, type_subname, defaulted = r
|
119
128
|
|
120
129
|
@overloaded ||= !overload.nil?
|
121
130
|
# if not overloaded then store arguments at key 0
|
@@ -136,7 +145,7 @@ module PLSQL
|
|
136
145
|
# then generate unique ID from object_name and overload
|
137
146
|
subprogram_id ||= "#{object_name.hash % 10000}#{overload}"
|
138
147
|
tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
|
139
|
-
elsif data_type !=
|
148
|
+
elsif data_type != "PL/SQL RECORD"
|
140
149
|
# raise exception only when there are no overloaded procedure definitions
|
141
150
|
# (as probably this overload will not be used at all)
|
142
151
|
raise ArgumentError, "Parameter type #{sql_type_name} definition inside package is not supported, use CREATE TYPE outside package" if overload == 0
|
@@ -144,18 +153,19 @@ module PLSQL
|
|
144
153
|
end
|
145
154
|
|
146
155
|
argument_metadata = {
|
147
|
-
:
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
158
|
-
:
|
156
|
+
position: position && position.to_i,
|
157
|
+
data_type: data_type,
|
158
|
+
in_out: in_out,
|
159
|
+
data_length: data_length && data_length.to_i,
|
160
|
+
data_precision: data_precision && data_precision.to_i,
|
161
|
+
data_scale: data_scale && data_scale.to_i,
|
162
|
+
char_used: char_used,
|
163
|
+
char_length: char_length && char_length.to_i,
|
164
|
+
type_owner: type_owner,
|
165
|
+
type_name: type_name,
|
166
|
+
type_subname: type_subname,
|
167
|
+
sql_type_name: sql_type_name,
|
168
|
+
defaulted: defaulted
|
159
169
|
}
|
160
170
|
if tmp_table_name
|
161
171
|
@tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
|
@@ -163,14 +173,14 @@ module PLSQL
|
|
163
173
|
|
164
174
|
if composite_type?(data_type)
|
165
175
|
case data_type
|
166
|
-
when
|
176
|
+
when "PL/SQL RECORD"
|
167
177
|
argument_metadata[:fields] = {}
|
168
178
|
end
|
169
179
|
previous_level_argument_metadata[data_level] = argument_metadata
|
170
180
|
end
|
171
181
|
|
172
182
|
# if function has return value
|
173
|
-
if argument_name.nil? && data_level == 0 && in_out ==
|
183
|
+
if argument_name.nil? && data_level == 0 && in_out == "OUT"
|
174
184
|
@return[overload] = argument_metadata
|
175
185
|
# if parameter
|
176
186
|
else
|
@@ -181,9 +191,9 @@ module PLSQL
|
|
181
191
|
# or lower level part of composite type
|
182
192
|
else
|
183
193
|
case previous_level_argument_metadata[data_level - 1][:data_type]
|
184
|
-
when
|
194
|
+
when "PL/SQL RECORD"
|
185
195
|
previous_level_argument_metadata[data_level - 1][:fields][argument_name.downcase.to_sym] = argument_metadata
|
186
|
-
when
|
196
|
+
when "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"
|
187
197
|
previous_level_argument_metadata[data_level - 1][:element] = argument_metadata
|
188
198
|
end
|
189
199
|
end
|
@@ -195,11 +205,104 @@ module PLSQL
|
|
195
205
|
construct_argument_list_for_overloads
|
196
206
|
end
|
197
207
|
|
208
|
+
# get procedure argument metadata from data dictionary
|
209
|
+
def get_argument_metadata_from_18c #:nodoc:
|
210
|
+
@arguments = {}
|
211
|
+
@argument_list = {}
|
212
|
+
@out_list = {}
|
213
|
+
@return = {}
|
214
|
+
@overloaded = false
|
215
|
+
|
216
|
+
# store tmp tables for each overload for table parameters with types defined inside packages
|
217
|
+
@tmp_table_names = {}
|
218
|
+
# store if tmp tables are created for specific overload
|
219
|
+
@tmp_tables_created = {}
|
220
|
+
|
221
|
+
@schema.select_all(
|
222
|
+
"SELECT subprogram_id, object_name, TO_NUMBER(overload), argument_name, position,
|
223
|
+
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
224
|
+
char_length, type_owner, nvl(type_subname, type_name),
|
225
|
+
decode(type_object_type, 'PACKAGE', type_name, null), type_object_type, defaulted
|
226
|
+
FROM all_arguments
|
227
|
+
WHERE object_id = :object_id
|
228
|
+
AND owner = :owner
|
229
|
+
AND object_name = :procedure_name
|
230
|
+
ORDER BY overload, sequence",
|
231
|
+
@object_id, @schema_name, @procedure
|
232
|
+
) do |r|
|
233
|
+
|
234
|
+
subprogram_id, object_name, overload, argument_name, position,
|
235
|
+
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
236
|
+
char_length, type_owner, type_name, type_package, type_object_type, defaulted = r
|
237
|
+
|
238
|
+
@overloaded ||= !overload.nil?
|
239
|
+
# if not overloaded then store arguments at key 0
|
240
|
+
overload ||= 0
|
241
|
+
@arguments[overload] ||= {}
|
242
|
+
@return[overload] ||= nil
|
243
|
+
@tmp_table_names[overload] ||= []
|
244
|
+
|
245
|
+
sql_type_name = build_sql_type_name(type_owner, type_package, type_name)
|
246
|
+
|
247
|
+
tmp_table_name = nil
|
248
|
+
# type defined inside package
|
249
|
+
if type_package
|
250
|
+
if collection_type?(data_type)
|
251
|
+
tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
argument_metadata = {
|
256
|
+
position: position && position.to_i,
|
257
|
+
data_type: data_type,
|
258
|
+
in_out: in_out,
|
259
|
+
data_length: data_length && data_length.to_i,
|
260
|
+
data_precision: data_precision && data_precision.to_i,
|
261
|
+
data_scale: data_scale && data_scale.to_i,
|
262
|
+
char_used: char_used,
|
263
|
+
char_length: char_length && char_length.to_i,
|
264
|
+
type_owner: type_owner,
|
265
|
+
type_name: type_name,
|
266
|
+
# TODO: should be renamed to type_package, when support for legacy database versions is dropped
|
267
|
+
# due to the explicit change declaration of types in oracle plsql_type-catalogs (type_package + type_name),
|
268
|
+
# the assignment of type + subtype was switched here for 18c and beyond
|
269
|
+
type_subname: type_package,
|
270
|
+
sql_type_name: sql_type_name,
|
271
|
+
defaulted: defaulted,
|
272
|
+
type_object_type: type_object_type
|
273
|
+
}
|
274
|
+
if tmp_table_name
|
275
|
+
@tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
|
276
|
+
end
|
277
|
+
|
278
|
+
if composite_type?(data_type)
|
279
|
+
case data_type
|
280
|
+
when "PL/SQL RECORD", "REF CURSOR"
|
281
|
+
argument_metadata[:fields] = get_field_definitions(argument_metadata)
|
282
|
+
when "PL/SQL TABLE", "TABLE", "VARRAY"
|
283
|
+
argument_metadata[:element] = get_element_definition(argument_metadata)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# if function has return value
|
288
|
+
if argument_name.nil? && in_out == "OUT"
|
289
|
+
@return[overload] = argument_metadata
|
290
|
+
else
|
291
|
+
# sometime there are empty IN arguments in all_arguments view for procedures without arguments (e.g. for DBMS_OUTPUT.DISABLE)
|
292
|
+
@arguments[overload][argument_name.downcase.to_sym] = argument_metadata if argument_name
|
293
|
+
end
|
294
|
+
end
|
295
|
+
# if procedure is without arguments then create default empty argument list for default overload
|
296
|
+
@arguments[0] = {} if @arguments.keys.empty?
|
297
|
+
|
298
|
+
construct_argument_list_for_overloads
|
299
|
+
end
|
300
|
+
|
198
301
|
def construct_argument_list_for_overloads #:nodoc:
|
199
302
|
@overloads = @arguments.keys.sort
|
200
303
|
@overloads.each do |overload|
|
201
|
-
@argument_list[overload] = @arguments[overload].keys.sort {|k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position]}
|
202
|
-
@out_list[overload] = @argument_list[overload].select {|k| @arguments[overload][k][:in_out] =~ /OUT/}
|
304
|
+
@argument_list[overload] = @arguments[overload].keys.sort { |k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position] }
|
305
|
+
@out_list[overload] = @argument_list[overload].select { |k| @arguments[overload][k][:in_out] =~ /OUT/ }
|
203
306
|
end
|
204
307
|
end
|
205
308
|
|
@@ -207,19 +310,19 @@ module PLSQL
|
|
207
310
|
return if @tmp_tables_created.nil? || @tmp_tables_created[overload]
|
208
311
|
@tmp_table_names[overload] && @tmp_table_names[overload].each do |table_name, argument_metadata|
|
209
312
|
sql = "CREATE GLOBAL TEMPORARY TABLE #{table_name} (\n"
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
313
|
+
element_metadata = argument_metadata[:element]
|
314
|
+
case element_metadata[:data_type]
|
315
|
+
when "PL/SQL RECORD"
|
316
|
+
fields_metadata = element_metadata[:fields]
|
317
|
+
fields_sorted_by_position = fields_metadata.keys.sort_by { |k| fields_metadata[k][:position] }
|
318
|
+
sql << fields_sorted_by_position.map do |field|
|
319
|
+
metadata = fields_metadata[field]
|
320
|
+
"#{field} #{ProcedureCommon.type_to_sql(metadata)}"
|
321
|
+
end.join(",\n")
|
322
|
+
else
|
323
|
+
sql << "element #{ProcedureCommon.type_to_sql(element_metadata)}"
|
324
|
+
end
|
325
|
+
sql << ",\ni__ NUMBER(38)\n"
|
223
326
|
sql << ") ON COMMIT PRESERVE ROWS\n"
|
224
327
|
sql_block = "DECLARE\nPRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\nEXECUTE IMMEDIATE :sql;\nEND;\n"
|
225
328
|
@schema.execute sql_block, sql
|
@@ -227,12 +330,191 @@ module PLSQL
|
|
227
330
|
@tmp_tables_created[overload] = true
|
228
331
|
end
|
229
332
|
|
230
|
-
|
333
|
+
def build_sql_type_name(type_owner, type_package, type_name) #:nodoc:
|
334
|
+
if type_owner == nil || type_owner == "PUBLIC"
|
335
|
+
type_owner_res = ""
|
336
|
+
else
|
337
|
+
type_owner_res = "#{type_owner}."
|
338
|
+
end
|
339
|
+
|
340
|
+
if type_package == nil
|
341
|
+
type_name_res = type_name
|
342
|
+
else
|
343
|
+
type_name_res = "#{type_package}.#{type_name}"
|
344
|
+
end
|
345
|
+
type_name_res && "#{type_owner_res}#{type_name_res}"
|
346
|
+
end
|
347
|
+
|
348
|
+
def get_field_definitions(argument_metadata) #:nodoc:
|
349
|
+
fields = {}
|
350
|
+
case argument_metadata[:type_object_type]
|
351
|
+
when "PACKAGE"
|
352
|
+
@schema.select_all(
|
353
|
+
"SELECT attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, length, precision, scale, char_used
|
354
|
+
FROM ALL_PLSQL_TYPES t, ALL_PLSQL_TYPE_ATTRS ta
|
355
|
+
WHERE t.OWNER = :owner AND t.type_name = :type_name AND t.package_name = :package_name
|
356
|
+
AND ta.OWNER = t.owner AND ta.TYPE_NAME = t.TYPE_NAME AND ta.PACKAGE_NAME = t.PACKAGE_NAME
|
357
|
+
ORDER BY attr_no",
|
358
|
+
@schema_name, argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
|
359
|
+
|
360
|
+
attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, attr_length, attr_precision, attr_scale, attr_char_used = r
|
361
|
+
|
362
|
+
fields[attr_name.downcase.to_sym] = {
|
363
|
+
position: attr_no.to_i,
|
364
|
+
data_type: attr_type_owner == nil ? attr_type_name : get_composite_type(attr_type_owner, attr_type_name, attr_type_package),
|
365
|
+
in_out: argument_metadata[:in_out],
|
366
|
+
data_length: attr_length && attr_length.to_i,
|
367
|
+
data_precision: attr_precision && attr_precision.to_i,
|
368
|
+
data_scale: attr_scale && attr_scale.to_i,
|
369
|
+
char_used: attr_char_used == nil ? "0" : attr_char_used,
|
370
|
+
char_length: attr_char_used && attr_length && attr_length.to_i,
|
371
|
+
type_owner: attr_type_owner,
|
372
|
+
type_name: attr_type_owner && attr_type_name,
|
373
|
+
type_subname: attr_type_package,
|
374
|
+
sql_type_name: attr_type_owner && build_sql_type_name(attr_type_owner, attr_type_package, attr_type_name),
|
375
|
+
defaulted: argument_metadata[:defaulted]
|
376
|
+
}
|
377
|
+
|
378
|
+
if fields[attr_name.downcase.to_sym][:data_type] == "TABLE" && fields[attr_name.downcase.to_sym][:type_subname] != nil
|
379
|
+
fields[attr_name.downcase.to_sym][:fields] = get_field_definitions(fields[attr_name.downcase.to_sym])
|
380
|
+
end
|
381
|
+
end
|
382
|
+
when "TABLE", "VIEW"
|
383
|
+
@schema.select_all(
|
384
|
+
"SELECT column_id, column_name, data_type, data_length, data_precision, data_scale, char_length, char_used
|
385
|
+
FROM ALL_TAB_COLS WHERE OWNER = :owner AND TABLE_NAME = :type_name
|
386
|
+
ORDER BY column_id",
|
387
|
+
@schema_name, argument_metadata[:type_name]) do |r|
|
388
|
+
|
389
|
+
col_no, col_name, col_type_name, col_length, col_precision, col_scale, col_char_length, col_char_used = r
|
390
|
+
|
391
|
+
fields[col_name.downcase.to_sym] = {
|
392
|
+
position: col_no.to_i,
|
393
|
+
data_type: col_type_name,
|
394
|
+
in_out: argument_metadata[:in_out],
|
395
|
+
data_length: col_length && col_length.to_i,
|
396
|
+
data_precision: col_precision && col_precision.to_i,
|
397
|
+
data_scale: col_scale && col_scale.to_i,
|
398
|
+
char_used: col_char_used == nil ? "0" : col_char_used,
|
399
|
+
char_length: col_char_length && col_char_length.to_i,
|
400
|
+
type_owner: nil,
|
401
|
+
type_name: nil,
|
402
|
+
type_subname: nil,
|
403
|
+
sql_type_name: nil,
|
404
|
+
defaulted: argument_metadata[:defaulted]
|
405
|
+
}
|
406
|
+
end
|
407
|
+
end
|
408
|
+
fields
|
409
|
+
end
|
410
|
+
|
411
|
+
def get_element_definition(argument_metadata) #:nodoc:
|
412
|
+
element_metadata = {}
|
413
|
+
if collection_type?(argument_metadata[:data_type])
|
414
|
+
case argument_metadata[:type_object_type]
|
415
|
+
when "PACKAGE"
|
416
|
+
r = @schema.select_first(
|
417
|
+
"SELECT elem_type_owner, elem_type_name, elem_type_package, length, precision, scale, char_used, index_by
|
418
|
+
FROM ALL_PLSQL_COLL_TYPES t
|
419
|
+
WHERE t.OWNER = :owner AND t.TYPE_NAME = :type_name AND t.PACKAGE_NAME = :package_name",
|
420
|
+
@schema_name, argument_metadata[:type_name], argument_metadata[:type_subname])
|
421
|
+
|
422
|
+
elem_type_owner, elem_type_name, elem_type_package, elem_length, elem_precision, elem_scale, elem_char_used, index_by = r
|
423
|
+
|
424
|
+
if index_by == "VARCHAR2"
|
425
|
+
raise ArgumentError, "Index-by Varchar-Table (associative array) #{argument_metadata[:type_name]} is not supported"
|
426
|
+
end
|
427
|
+
|
428
|
+
element_metadata = {
|
429
|
+
position: 1,
|
430
|
+
data_type: if elem_type_owner == nil
|
431
|
+
elem_type_name
|
432
|
+
else
|
433
|
+
elem_type_package != nil ? "PL/SQL RECORD" : "OBJECT"
|
434
|
+
end,
|
435
|
+
in_out: argument_metadata[:in_out],
|
436
|
+
data_length: elem_length && elem_length.to_i,
|
437
|
+
data_precision: elem_precision && elem_precision.to_i,
|
438
|
+
data_scale: elem_scale && elem_scale.to_i,
|
439
|
+
char_used: elem_char_used,
|
440
|
+
char_length: elem_char_used && elem_length && elem_length.to_i,
|
441
|
+
type_owner: elem_type_owner,
|
442
|
+
type_name: elem_type_name,
|
443
|
+
type_subname: elem_type_package,
|
444
|
+
sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, elem_type_package, elem_type_name),
|
445
|
+
type_object_type: elem_type_package != nil ? "PACKAGE" : nil,
|
446
|
+
defaulted: argument_metadata[:defaulted]
|
447
|
+
}
|
448
|
+
|
449
|
+
if elem_type_package != nil
|
450
|
+
element_metadata[:fields] = get_field_definitions(element_metadata)
|
451
|
+
end
|
452
|
+
when "TYPE"
|
453
|
+
r = @schema.select_first(
|
454
|
+
"SELECT elem_type_owner, elem_type_name, length, precision, scale, char_used
|
455
|
+
FROM ALL_COLL_TYPES t
|
456
|
+
WHERE t.owner = :owner AND t.TYPE_NAME = :type_name",
|
457
|
+
@schema_name, argument_metadata[:type_name]
|
458
|
+
)
|
459
|
+
elem_type_owner, elem_type_name, elem_length, elem_precision, elem_scale, elem_char_used = r
|
460
|
+
|
461
|
+
element_metadata = {
|
462
|
+
position: 1,
|
463
|
+
data_type: elem_type_owner == nil ? elem_type_name : "OBJECT",
|
464
|
+
in_out: argument_metadata[:in_out],
|
465
|
+
data_length: elem_length && elem_length.to_i,
|
466
|
+
data_precision: elem_precision && elem_precision.to_i,
|
467
|
+
data_scale: elem_scale && elem_scale.to_i,
|
468
|
+
char_used: elem_char_used,
|
469
|
+
char_length: elem_char_used && elem_length && elem_length.to_i,
|
470
|
+
type_owner: elem_type_owner,
|
471
|
+
type_name: elem_type_name,
|
472
|
+
type_subname: nil,
|
473
|
+
sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, nil, elem_type_name),
|
474
|
+
defaulted: argument_metadata[:defaulted]
|
475
|
+
}
|
476
|
+
end
|
477
|
+
else
|
478
|
+
element_metadata = {
|
479
|
+
position: 1,
|
480
|
+
data_type: "PL/SQL RECORD",
|
481
|
+
in_out: argument_metadata[:in_out],
|
482
|
+
data_length: nil,
|
483
|
+
data_precision: nil,
|
484
|
+
data_scale: nil,
|
485
|
+
char_used: "B",
|
486
|
+
char_length: 0,
|
487
|
+
type_owner: argument_metadata[:type_owner],
|
488
|
+
type_name: argument_metadata[:type_name],
|
489
|
+
type_subname: argument_metadata[:type_subname],
|
490
|
+
sql_type_name: build_sql_type_name(argument_metadata[:type_owner], argument_metadata[:type_subname], argument_metadata[:type_name]),
|
491
|
+
defaulted: argument_metadata[:defaulted]
|
492
|
+
}
|
493
|
+
|
494
|
+
if element_metadata[:type_subname] != nil
|
495
|
+
element_metadata[:fields] = get_field_definitions(element_metadata)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
element_metadata
|
499
|
+
end
|
500
|
+
|
501
|
+
def get_composite_type(type_owner, type_name, type_package)
|
502
|
+
r = @schema.select_first("SELECT typecode FROM all_plsql_types WHERE owner = :owner AND type_name = :type_name AND package_name = :type_package
|
503
|
+
UNION ALL
|
504
|
+
SELECT typecode FROM all_types WHERE owner = :owner AND type_name = :type_name",
|
505
|
+
type_owner, type_name, type_package, type_owner, type_name)
|
506
|
+
typecode = r[0]
|
507
|
+
raise ArgumentError, "#{type_name} type #{build_sql_type_name(type_owner, type_package, type_name)} definition inside package is not supported as part of other type definition," <<
|
508
|
+
" use CREATE TYPE outside package" if typecode == "COLLECTION"
|
509
|
+
typecode
|
510
|
+
end
|
511
|
+
|
512
|
+
PLSQL_COMPOSITE_TYPES = ["PL/SQL RECORD", "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"].freeze
|
231
513
|
def composite_type?(data_type) #:nodoc:
|
232
514
|
PLSQL_COMPOSITE_TYPES.include? data_type
|
233
515
|
end
|
234
516
|
|
235
|
-
PLSQL_COLLECTION_TYPES = [
|
517
|
+
PLSQL_COLLECTION_TYPES = ["PL/SQL TABLE", "TABLE", "VARRAY"].freeze
|
236
518
|
def collection_type?(data_type) #:nodoc:
|
237
519
|
PLSQL_COLLECTION_TYPES.include? data_type
|
238
520
|
end
|
@@ -263,7 +545,5 @@ module PLSQL
|
|
263
545
|
call = ProcedureCall.new(self, args)
|
264
546
|
call.exec(&block)
|
265
547
|
end
|
266
|
-
|
267
548
|
end
|
268
|
-
|
269
549
|
end
|