@@ -83,6 +83,14 @@ def self.type_to_sql(metadata) #:nodoc:
83
83
84
84
# get procedure argument metadata from data dictionary
85
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:
86
94
@arguments = { }
87
95
@argument_list = { }
88
96
@out_list = { }
@@ -197,6 +205,99 @@ def get_argument_metadata #:nodoc:
197
205
construct_argument_list_for_overloads
198
206
end
199
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
+
200
301
def construct_argument_list_for_overloads #:nodoc:
201
302
@overloads = @arguments . keys . sort
202
303
@overloads . each do |overload |
@@ -229,6 +330,185 @@ def ensure_tmp_tables_created(overload) #:nodoc:
229
330
@tmp_tables_created [ overload ] = true
230
331
end
231
332
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
+
232
512
PLSQL_COMPOSITE_TYPES = [ "PL/SQL RECORD" , "PL/SQL TABLE" , "TABLE" , "VARRAY" , "REF CURSOR" ] . freeze
233
513
def composite_type? ( data_type ) #:nodoc:
234
514
PLSQL_COMPOSITE_TYPES . include? data_type
0 commit comments