ruby-plsql 0.9.9-java

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.
@@ -0,0 +1,584 @@
1
+ module PLSQL
2
+ module ProcedureClassMethods # :nodoc:
3
+ def find(schema, procedure, package = nil, override_schema_name = nil)
4
+ if package.nil?
5
+ if (row = schema.select_first(
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')",
13
+ schema.schema_name, procedure.to_s.upcase))
14
+ new(schema, procedure, nil, nil, row[0])
15
+ # search for synonym
16
+ elsif (row = schema.select_first(
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)",
27
+ schema.schema_name, procedure.to_s.upcase))
28
+ new(schema, row[1], nil, row[0], row[2])
29
+ else
30
+ nil
31
+ end
32
+ elsif package && (row = schema.select_first(
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'",
42
+ override_schema_name || schema.schema_name, package, procedure.to_s.upcase))
43
+ new(schema, procedure, package, override_schema_name, row[0])
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def procedure_object_id_src(schema)
52
+ (schema.connection.database_version <=> [11, 1, 0, 0]) >= 0 ? "p" : "o"
53
+ end
54
+ end
55
+
56
+ module ProcedureCommon # :nodoc:
57
+ attr_reader :arguments, :argument_list, :out_list, :return
58
+ attr_reader :schema, :schema_name, :package, :procedure
59
+
60
+ # return type string from metadata that can be used in DECLARE block or table definition
61
+ def self.type_to_sql(metadata) # :nodoc:
62
+ case metadata[:data_type]
63
+ when "NUMBER"
64
+ precision, scale = metadata[:data_precision], metadata[:data_scale]
65
+ "NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}" : ""})" : ""}"
66
+ when "VARCHAR", "VARCHAR2", "CHAR"
67
+ length = case metadata[:char_used]
68
+ when "C" then "#{metadata[:char_length]} CHAR"
69
+ when "B" then "#{metadata[:data_length]} BYTE"
70
+ else
71
+ metadata[:data_length]
72
+ end
73
+ "#{metadata[:data_type]}#{length && "(#{length})"}"
74
+ when "NVARCHAR2", "NCHAR"
75
+ length = metadata[:char_length]
76
+ "#{metadata[:data_type]}#{length && "(#{length})"}"
77
+ when "PL/SQL TABLE", "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
78
+ metadata[:sql_type_name]
79
+ else
80
+ metadata[:data_type]
81
+ end
82
+ end
83
+
84
+ # get procedure argument metadata from data dictionary
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:
94
+ @arguments = {}
95
+ @argument_list = {}
96
+ @out_list = {}
97
+ @return = {}
98
+ @overloaded = false
99
+
100
+ # store reference to previous level record or collection metadata
101
+ previous_level_argument_metadata = {}
102
+
103
+ # store tmp tables for each overload for table parameters with types defined inside packages
104
+ @tmp_table_names = {}
105
+ # store if tmp tables are created for specific overload
106
+ @tmp_tables_created = {}
107
+
108
+ # subprogram_id column is available just from version 10g
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"
112
+
113
+ @schema.select_all(
114
+ "SELECT #{subprogram_id_column}, object_name, TO_NUMBER(overload), argument_name, position, data_level,
115
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
116
+ char_length, type_owner, type_name, type_subname, #{defaulted_column}
117
+ FROM all_arguments
118
+ WHERE object_id = :object_id
119
+ AND owner = :owner
120
+ AND object_name = :procedure_name
121
+ ORDER BY overload, sequence",
122
+ @object_id, @schema_name, @procedure
123
+ ) do |r|
124
+
125
+ subprogram_id, object_name, overload, argument_name, position, data_level,
126
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
127
+ char_length, type_owner, type_name, type_subname, defaulted = r
128
+
129
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
130
+ data_type = "PL/SQL BOOLEAN" if data_type == "BOOLEAN"
131
+
132
+ @overloaded ||= !overload.nil?
133
+ # if not overloaded then store arguments at key 0
134
+ overload ||= 0
135
+ @arguments[overload] ||= {}
136
+ @return[overload] ||= nil
137
+ @tmp_table_names[overload] ||= []
138
+
139
+ sql_type_name = type_owner && "#{type_owner == 'PUBLIC' ? nil : "#{type_owner}."}#{type_name}#{type_subname ? ".#{type_subname}" : nil}"
140
+
141
+ tmp_table_name = nil
142
+ # type defined inside package
143
+ if type_subname
144
+ if collection_type?(data_type)
145
+ raise ArgumentError, "#{data_type} type #{sql_type_name} definition inside package is not supported as part of other type definition," <<
146
+ " use CREATE TYPE outside package" if data_level > 0
147
+ # if subprogram_id was not supported by all_arguments view
148
+ # then generate unique ID from object_name and overload
149
+ subprogram_id ||= "#{object_name.hash % 10000}#{overload}"
150
+ tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
151
+ elsif data_type != "PL/SQL RECORD"
152
+ # raise exception only when there are no overloaded procedure definitions
153
+ # (as probably this overload will not be used at all)
154
+ raise ArgumentError, "Parameter type #{sql_type_name} definition inside package is not supported, use CREATE TYPE outside package" if overload == 0
155
+ end
156
+ end
157
+
158
+ argument_metadata = {
159
+ position: position && position.to_i,
160
+ data_type: data_type,
161
+ in_out: in_out,
162
+ data_length: data_length && data_length.to_i,
163
+ data_precision: data_precision && data_precision.to_i,
164
+ data_scale: data_scale && data_scale.to_i,
165
+ char_used: char_used,
166
+ char_length: char_length && char_length.to_i,
167
+ type_owner: type_owner,
168
+ type_name: type_name,
169
+ type_subname: type_subname,
170
+ sql_type_name: sql_type_name,
171
+ defaulted: defaulted
172
+ }
173
+ if tmp_table_name
174
+ @tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
175
+ end
176
+
177
+ if composite_type?(data_type)
178
+ case data_type
179
+ when "PL/SQL RECORD"
180
+ argument_metadata[:fields] = {}
181
+ end
182
+ previous_level_argument_metadata[data_level] = argument_metadata
183
+ end
184
+
185
+ # if function has return value
186
+ if argument_name.nil? && data_level == 0 && in_out == "OUT"
187
+ @return[overload] = argument_metadata
188
+ # if parameter
189
+ else
190
+ # top level parameter
191
+ if data_level == 0
192
+ # sometime there are empty IN arguments in all_arguments view for procedures without arguments (e.g. for DBMS_OUTPUT.DISABLE)
193
+ @arguments[overload][argument_name.downcase.to_sym] = argument_metadata if argument_name
194
+ # or lower level part of composite type
195
+ else
196
+ case previous_level_argument_metadata[data_level - 1][:data_type]
197
+ when "PL/SQL RECORD"
198
+ previous_level_argument_metadata[data_level - 1][:fields][argument_name.downcase.to_sym] = argument_metadata
199
+ when "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"
200
+ previous_level_argument_metadata[data_level - 1][:element] = argument_metadata
201
+ end
202
+ end
203
+ end
204
+ end
205
+ # if procedure is without arguments then create default empty argument list for default overload
206
+ @arguments[0] = {} if @arguments.keys.empty?
207
+
208
+ construct_argument_list_for_overloads
209
+ end
210
+
211
+ # get procedure argument metadata from data dictionary
212
+ def get_argument_metadata_from_18c # :nodoc:
213
+ @arguments = {}
214
+ @argument_list = {}
215
+ @out_list = {}
216
+ @return = {}
217
+ @overloaded = false
218
+
219
+ # store tmp tables for each overload for table parameters with types defined inside packages
220
+ @tmp_table_names = {}
221
+ # store if tmp tables are created for specific overload
222
+ @tmp_tables_created = {}
223
+
224
+ @schema.select_all(
225
+ "SELECT subprogram_id, object_name, TO_NUMBER(overload), argument_name, position,
226
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
227
+ char_length, type_owner, nvl(type_subname, type_name),
228
+ decode(type_object_type, 'PACKAGE', type_name, null), type_object_type, defaulted
229
+ FROM all_arguments
230
+ WHERE object_id = :object_id
231
+ AND owner = :owner
232
+ AND object_name = :procedure_name
233
+ ORDER BY overload, sequence",
234
+ @object_id, @schema_name, @procedure
235
+ ) do |r|
236
+
237
+ subprogram_id, _object_name, overload, argument_name, position,
238
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
239
+ char_length, type_owner, type_name, type_package, type_object_type, defaulted = r
240
+
241
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
242
+ data_type = "PL/SQL BOOLEAN" if data_type == "BOOLEAN"
243
+
244
+ @overloaded ||= !overload.nil?
245
+ # if not overloaded then store arguments at key 0
246
+ overload ||= 0
247
+ @arguments[overload] ||= {}
248
+ @return[overload] ||= nil
249
+ @tmp_table_names[overload] ||= []
250
+
251
+ sql_type_name = build_sql_type_name(type_owner, type_package, type_name)
252
+
253
+ tmp_table_name = nil
254
+ # type defined inside package
255
+ if type_package
256
+ if collection_type?(data_type)
257
+ tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
258
+ end
259
+ end
260
+
261
+ argument_metadata = {
262
+ position: position && position.to_i,
263
+ data_type: data_type,
264
+ in_out: in_out,
265
+ data_length: data_length && data_length.to_i,
266
+ data_precision: data_precision && data_precision.to_i,
267
+ data_scale: data_scale && data_scale.to_i,
268
+ char_used: char_used,
269
+ char_length: char_length && char_length.to_i,
270
+ type_owner: type_owner,
271
+ type_name: type_name,
272
+ # TODO: should be renamed to type_package, when support for legacy database versions is dropped
273
+ # due to the explicit change declaration of types in oracle plsql_type-catalogs (type_package + type_name),
274
+ # the assignment of type + subtype was switched here for 18c and beyond
275
+ type_subname: type_package,
276
+ sql_type_name: sql_type_name,
277
+ defaulted: defaulted,
278
+ type_object_type: type_object_type
279
+ }
280
+ if tmp_table_name
281
+ @tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
282
+ end
283
+
284
+ if composite_type?(data_type)
285
+ case data_type
286
+ when "PL/SQL RECORD", "REF CURSOR"
287
+ argument_metadata[:fields] = get_field_definitions(argument_metadata)
288
+ when "PL/SQL TABLE", "TABLE", "VARRAY"
289
+ argument_metadata[:element] = get_element_definition(argument_metadata)
290
+ end
291
+ end
292
+
293
+ # if function has return value
294
+ if argument_name.nil? && in_out == "OUT"
295
+ @return[overload] = argument_metadata
296
+ else
297
+ # sometime there are empty IN arguments in all_arguments view for procedures without arguments (e.g. for DBMS_OUTPUT.DISABLE)
298
+ @arguments[overload][argument_name.downcase.to_sym] = argument_metadata if argument_name
299
+ end
300
+ end
301
+ # if procedure is without arguments then create default empty argument list for default overload
302
+ @arguments[0] = {} if @arguments.keys.empty?
303
+
304
+ construct_argument_list_for_overloads
305
+ end
306
+
307
+ def construct_argument_list_for_overloads # :nodoc:
308
+ @overloads = @arguments.keys.sort
309
+ @overloads.each do |overload|
310
+ @argument_list[overload] = @arguments[overload].keys.sort { |k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position] }
311
+ @out_list[overload] = @argument_list[overload].select { |k| @arguments[overload][k][:in_out] =~ /OUT/ }
312
+ end
313
+ end
314
+
315
+ def ensure_tmp_tables_created(overload) # :nodoc:
316
+ return if @tmp_tables_created.nil? || @tmp_tables_created[overload]
317
+ @tmp_table_names[overload] && @tmp_table_names[overload].each do |table_name, argument_metadata|
318
+ sql = +"CREATE GLOBAL TEMPORARY TABLE #{table_name} (\n"
319
+ element_metadata = argument_metadata[:element]
320
+ case element_metadata[:data_type]
321
+ when "PL/SQL RECORD"
322
+ fields_metadata = element_metadata[:fields]
323
+ fields_sorted_by_position = fields_metadata.keys.sort_by { |k| fields_metadata[k][:position] }
324
+ sql << fields_sorted_by_position.map do |field|
325
+ metadata = fields_metadata[field]
326
+ "#{field} #{ProcedureCommon.type_to_sql(metadata)}"
327
+ end.join(",\n")
328
+ else
329
+ sql << "element #{ProcedureCommon.type_to_sql(element_metadata)}"
330
+ end
331
+ sql << ",\ni__ NUMBER(38)\n"
332
+ sql << ") ON COMMIT PRESERVE ROWS\n"
333
+ sql_block = "DECLARE\nPRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\nEXECUTE IMMEDIATE :sql;\nEND;\n"
334
+ @schema.execute sql_block, sql
335
+ end
336
+ @tmp_tables_created[overload] = true
337
+ end
338
+
339
+ def build_sql_type_name(type_owner, type_package, type_name) # :nodoc:
340
+ if type_owner == nil || type_owner == "PUBLIC"
341
+ type_owner_res = ""
342
+ else
343
+ type_owner_res = "#{type_owner}."
344
+ end
345
+
346
+ if type_package == nil
347
+ type_name_res = type_name
348
+ else
349
+ type_name_res = "#{type_package}.#{type_name}"
350
+ end
351
+ type_name_res && "#{type_owner_res}#{type_name_res}"
352
+ end
353
+
354
+ def get_field_definitions(argument_metadata) # :nodoc:
355
+ fields = {}
356
+ case argument_metadata[:type_object_type]
357
+ when "PACKAGE"
358
+ @schema.select_all(
359
+ "SELECT attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, length, precision, scale, char_used
360
+ FROM ALL_PLSQL_TYPES t, ALL_PLSQL_TYPE_ATTRS ta
361
+ WHERE t.OWNER = :owner AND t.type_name = :type_name AND t.package_name = :package_name
362
+ AND ta.OWNER = t.owner AND ta.TYPE_NAME = t.TYPE_NAME AND ta.PACKAGE_NAME = t.PACKAGE_NAME
363
+ ORDER BY attr_no",
364
+ argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
365
+
366
+ attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, attr_length, attr_precision, attr_scale, attr_char_used = r
367
+
368
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
369
+ attr_type_name = "PL/SQL BOOLEAN" if attr_type_name == "BOOLEAN"
370
+
371
+ fields[attr_name.downcase.to_sym] = {
372
+ position: attr_no.to_i,
373
+ data_type: attr_type_owner == nil ? attr_type_name : get_composite_type(attr_type_owner, attr_type_name, attr_type_package),
374
+ in_out: argument_metadata[:in_out],
375
+ data_length: attr_length && attr_length.to_i,
376
+ data_precision: attr_precision && attr_precision.to_i,
377
+ data_scale: attr_scale && attr_scale.to_i,
378
+ char_used: attr_char_used == nil ? "0" : attr_char_used,
379
+ char_length: attr_char_used && attr_length && attr_length.to_i,
380
+ type_owner: attr_type_owner,
381
+ type_name: attr_type_owner && attr_type_name,
382
+ type_subname: attr_type_package,
383
+ sql_type_name: attr_type_owner && build_sql_type_name(attr_type_owner, attr_type_package, attr_type_name),
384
+ defaulted: argument_metadata[:defaulted]
385
+ }
386
+
387
+ if fields[attr_name.downcase.to_sym][:data_type] == "TABLE" && fields[attr_name.downcase.to_sym][:type_subname] != nil
388
+ fields[attr_name.downcase.to_sym][:fields] = get_field_definitions(fields[attr_name.downcase.to_sym])
389
+ end
390
+ end
391
+ when "TABLE", "VIEW"
392
+ @schema.select_all(
393
+ "SELECT column_id, column_name, data_type, data_length, data_precision, data_scale, char_length, char_used
394
+ FROM ALL_TAB_COLUMNS WHERE OWNER = :owner AND TABLE_NAME = :type_name
395
+ ORDER BY column_id",
396
+ argument_metadata[:type_owner], argument_metadata[:type_name]) do |r|
397
+
398
+ col_no, col_name, col_type_name, col_length, col_precision, col_scale, col_char_length, col_char_used = r
399
+
400
+ # remove precision (n) from data_type (returned for TIMESTAMPs and INTERVALs)
401
+ col_type_name = col_type_name.sub(/\(\d+\)/, "")
402
+
403
+ fields[col_name.downcase.to_sym] = {
404
+ position: col_no.to_i,
405
+ data_type: col_type_name,
406
+ in_out: argument_metadata[:in_out],
407
+ data_length: col_length && col_length.to_i,
408
+ data_precision: col_precision && col_precision.to_i,
409
+ data_scale: col_scale && col_scale.to_i,
410
+ char_used: col_char_used == nil ? "0" : col_char_used,
411
+ char_length: col_char_length && col_char_length.to_i,
412
+ type_owner: nil,
413
+ type_name: nil,
414
+ type_subname: nil,
415
+ sql_type_name: nil,
416
+ defaulted: argument_metadata[:defaulted]
417
+ }
418
+ end
419
+ end
420
+ fields
421
+ end
422
+
423
+ def get_element_definition(argument_metadata) # :nodoc:
424
+ element_metadata = {}
425
+ if collection_type?(argument_metadata[:data_type])
426
+ case argument_metadata[:type_object_type]
427
+ when "PACKAGE"
428
+ r = @schema.select_first(
429
+ "SELECT elem_type_owner, elem_type_name, elem_type_package, length, precision, scale, char_used, index_by
430
+ FROM ALL_PLSQL_COLL_TYPES t
431
+ WHERE t.OWNER = :owner AND t.TYPE_NAME = :type_name AND t.PACKAGE_NAME = :package_name",
432
+ argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname])
433
+
434
+ elem_type_owner, elem_type_name, elem_type_package, elem_length, elem_precision, elem_scale, elem_char_used, index_by = r
435
+
436
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
437
+ elem_type_name = "PL/SQL BOOLEAN" if elem_type_name == "BOOLEAN"
438
+
439
+ if index_by == "VARCHAR2"
440
+ raise ArgumentError, "Index-by Varchar-Table (associative array) #{argument_metadata[:type_name]} is not supported"
441
+ end
442
+
443
+ element_metadata = {
444
+ position: 1,
445
+ data_type: if elem_type_owner == nil
446
+ elem_type_name
447
+ else
448
+ elem_type_package != nil ? "PL/SQL RECORD" : "OBJECT"
449
+ end,
450
+ in_out: argument_metadata[:in_out],
451
+ data_length: elem_length && elem_length.to_i,
452
+ data_precision: elem_precision && elem_precision.to_i,
453
+ data_scale: elem_scale && elem_scale.to_i,
454
+ char_used: elem_char_used,
455
+ char_length: elem_char_used && elem_length && elem_length.to_i,
456
+ type_owner: elem_type_owner,
457
+ type_name: elem_type_name,
458
+ type_subname: elem_type_package,
459
+ sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, elem_type_package, elem_type_name),
460
+ type_object_type: elem_type_package != nil ? "PACKAGE" : nil,
461
+ defaulted: argument_metadata[:defaulted]
462
+ }
463
+
464
+ if elem_type_package != nil
465
+ element_metadata[:fields] = get_field_definitions(element_metadata)
466
+ elsif elem_type_name && elem_type_name =~ /\A(.+)%ROWTYPE\z/
467
+ # TABLE OF table%ROWTYPE: Oracle stores elem_type_name as "TABLE_NAME%ROWTYPE"
468
+ rowtype_table_name = $1
469
+ check_owner = elem_type_owner || @schema_name
470
+ object_type_row = @schema.select_first(
471
+ "SELECT object_type FROM ALL_OBJECTS WHERE owner = :owner AND object_name = :name AND object_type IN ('TABLE', 'VIEW')",
472
+ check_owner, rowtype_table_name)
473
+ if object_type_row
474
+ element_metadata[:type_owner] ||= check_owner
475
+ element_metadata[:type_name] = rowtype_table_name
476
+ element_metadata[:sql_type_name] = build_sql_type_name(check_owner, nil, rowtype_table_name)
477
+ element_metadata[:data_type] = "PL/SQL RECORD"
478
+ element_metadata[:type_object_type] = object_type_row[0]
479
+ element_metadata[:fields] = get_field_definitions(element_metadata)
480
+ else
481
+ raise ArgumentError, "Could not resolve #{check_owner}.#{rowtype_table_name} to a table or view for #{elem_type_name}"
482
+ end
483
+ end
484
+ when "TYPE"
485
+ r = @schema.select_first(
486
+ "SELECT elem_type_owner, elem_type_name, length, precision, scale, char_used
487
+ FROM ALL_COLL_TYPES t
488
+ WHERE t.owner = :owner AND t.TYPE_NAME = :type_name",
489
+ argument_metadata[:type_owner], argument_metadata[:type_name]
490
+ )
491
+ elem_type_owner, elem_type_name, elem_length, elem_precision, elem_scale, elem_char_used = r
492
+
493
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
494
+ elem_type_name = "PL/SQL BOOLEAN" if elem_type_name == "BOOLEAN"
495
+
496
+ element_metadata = {
497
+ position: 1,
498
+ data_type: elem_type_owner == nil ? elem_type_name : "OBJECT",
499
+ in_out: argument_metadata[:in_out],
500
+ data_length: elem_length && elem_length.to_i,
501
+ data_precision: elem_precision && elem_precision.to_i,
502
+ data_scale: elem_scale && elem_scale.to_i,
503
+ char_used: elem_char_used,
504
+ char_length: elem_char_used && elem_length && elem_length.to_i,
505
+ type_owner: elem_type_owner,
506
+ type_name: elem_type_name,
507
+ type_subname: nil,
508
+ sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, nil, elem_type_name),
509
+ defaulted: argument_metadata[:defaulted]
510
+ }
511
+ end
512
+ else
513
+ element_metadata = {
514
+ position: 1,
515
+ data_type: "PL/SQL RECORD",
516
+ in_out: argument_metadata[:in_out],
517
+ data_length: nil,
518
+ data_precision: nil,
519
+ data_scale: nil,
520
+ char_used: "B",
521
+ char_length: 0,
522
+ type_owner: argument_metadata[:type_owner],
523
+ type_name: argument_metadata[:type_name],
524
+ type_subname: argument_metadata[:type_subname],
525
+ sql_type_name: build_sql_type_name(argument_metadata[:type_owner], argument_metadata[:type_subname], argument_metadata[:type_name]),
526
+ defaulted: argument_metadata[:defaulted]
527
+ }
528
+
529
+ if element_metadata[:type_subname] != nil
530
+ element_metadata[:fields] = get_field_definitions(element_metadata)
531
+ end
532
+ end
533
+ element_metadata
534
+ end
535
+
536
+ def get_composite_type(type_owner, type_name, type_package)
537
+ r = @schema.select_first("SELECT typecode FROM all_plsql_types WHERE owner = :owner AND type_name = :type_name AND package_name = :type_package
538
+ UNION ALL
539
+ SELECT typecode FROM all_types WHERE owner = :owner AND type_name = :type_name",
540
+ type_owner, type_name, type_package, type_owner, type_name)
541
+ typecode = r[0]
542
+ 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," <<
543
+ " use CREATE TYPE outside package" if typecode == "COLLECTION"
544
+ typecode
545
+ end
546
+
547
+ PLSQL_COMPOSITE_TYPES = ["PL/SQL RECORD", "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"].freeze
548
+ def composite_type?(data_type) # :nodoc:
549
+ PLSQL_COMPOSITE_TYPES.include? data_type
550
+ end
551
+
552
+ PLSQL_COLLECTION_TYPES = ["PL/SQL TABLE", "TABLE", "VARRAY"].freeze
553
+ def collection_type?(data_type) # :nodoc:
554
+ PLSQL_COLLECTION_TYPES.include? data_type
555
+ end
556
+
557
+ def overloaded? # :nodoc:
558
+ @overloaded
559
+ end
560
+ end
561
+
562
+ class Procedure # :nodoc:
563
+ extend ProcedureClassMethods
564
+ include ProcedureCommon
565
+
566
+ attr_reader :arguments, :argument_list, :out_list, :return
567
+ attr_reader :schema, :schema_name, :package, :procedure
568
+
569
+ def initialize(schema, procedure, package, override_schema_name, object_id)
570
+ @schema = schema
571
+ @schema_name = override_schema_name || schema.schema_name
572
+ @procedure = procedure.to_s.upcase
573
+ @package = package
574
+ @object_id = object_id
575
+
576
+ get_argument_metadata
577
+ end
578
+
579
+ def exec(*args, &block)
580
+ call = ProcedureCall.new(self, args)
581
+ call.exec(&block)
582
+ end
583
+ end
584
+ end