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.
- checksums.yaml +7 -0
- data/History.txt +282 -0
- data/License.txt +20 -0
- data/README.md +263 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +230 -0
- data/lib/plsql/helpers.rb +7 -0
- data/lib/plsql/jdbc_connection.rb +588 -0
- data/lib/plsql/oci_connection.rb +324 -0
- data/lib/plsql/package.rb +87 -0
- data/lib/plsql/procedure.rb +584 -0
- data/lib/plsql/procedure_call.rb +626 -0
- data/lib/plsql/schema.rb +296 -0
- data/lib/plsql/sequence.rb +46 -0
- data/lib/plsql/sql_statements.rb +85 -0
- data/lib/plsql/table.rb +345 -0
- data/lib/plsql/type.rb +270 -0
- data/lib/plsql/variable.rb +143 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/plsql/view.rb +38 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +13 -0
- metadata +120 -0
|
@@ -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
|