ruby-plsql 0.5.3 → 0.8.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.
- 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
|