@@ -83,6 +83,14 @@ def self.type_to_sql(metadata) #:nodoc:
8383
8484 # get procedure argument metadata from data dictionary
8585 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:
8694 @arguments = { }
8795 @argument_list = { }
8896 @out_list = { }
@@ -197,6 +205,99 @@ def get_argument_metadata #:nodoc:
197205 construct_argument_list_for_overloads
198206 end
199207
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+
200301 def construct_argument_list_for_overloads #:nodoc:
201302 @overloads = @arguments . keys . sort
202303 @overloads . each do |overload |
@@ -229,6 +330,185 @@ def ensure_tmp_tables_created(overload) #:nodoc:
229330 @tmp_tables_created [ overload ] = true
230331 end
231332
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+
232512 PLSQL_COMPOSITE_TYPES = [ "PL/SQL RECORD" , "PL/SQL TABLE" , "TABLE" , "VARRAY" , "REF CURSOR" ] . freeze
233513 def composite_type? ( data_type ) #:nodoc:
234514 PLSQL_COMPOSITE_TYPES . include? data_type
0 commit comments