ruby-plsql 0.6.0 → 0.7.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/.codeclimate.yml +30 -0
- data/.github/stale.yml +37 -0
- data/.rubocop.yml +153 -0
- data/.travis.yml +20 -6
- data/.travis/oracle/download.sh +9 -10
- data/.travis/oracle/install.sh +6 -6
- data/Gemfile +13 -9
- data/History.txt +26 -0
- data/README.md +9 -5
- data/Rakefile +31 -26
- data/VERSION +1 -1
- data/Vagrantfile +2 -2
- 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/lib/plsql/connection.rb +16 -18
- data/lib/plsql/helpers.rb +1 -3
- data/lib/plsql/jdbc_connection.rb +66 -61
- data/lib/plsql/oci8_patches.rb +2 -2
- data/lib/plsql/oci_connection.rb +51 -69
- data/lib/plsql/package.rb +5 -8
- data/lib/plsql/procedure.rb +75 -78
- data/lib/plsql/procedure_call.rb +498 -501
- data/lib/plsql/schema.rb +95 -100
- data/lib/plsql/sequence.rb +10 -13
- data/lib/plsql/sql_statements.rb +9 -11
- data/lib/plsql/table.rb +59 -63
- data/lib/plsql/type.rb +71 -76
- data/lib/plsql/variable.rb +89 -94
- data/lib/plsql/version.rb +1 -1
- data/lib/plsql/view.rb +16 -19
- data/ruby-plsql.gemspec +41 -37
- data/spec/plsql/connection_spec.rb +67 -67
- data/spec/plsql/package_spec.rb +15 -15
- data/spec/plsql/procedure_spec.rb +286 -233
- data/spec/plsql/schema_spec.rb +22 -23
- data/spec/plsql/sequence_spec.rb +2 -2
- data/spec/plsql/sql_statements_spec.rb +5 -5
- data/spec/plsql/table_spec.rb +77 -77
- data/spec/plsql/type_spec.rb +23 -29
- data/spec/plsql/variable_spec.rb +59 -59
- data/spec/plsql/version_spec.rb +4 -4
- data/spec/plsql/view_spec.rb +42 -42
- data/spec/spec_helper.rb +37 -29
- data/spec/support/test_db.rb +12 -13
- metadata +44 -26
- data/.travis/oracle/LICENSE +0 -5
- data/.travis/oracle/README.md +0 -64
- data/.travis/oracle/download.js +0 -100
data/lib/plsql/procedure_call.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module PLSQL
|
2
2
|
class ProcedureCall #:nodoc:
|
3
|
-
|
4
3
|
def initialize(procedure, args = [], options = {})
|
5
4
|
@procedure = procedure
|
6
5
|
@schema = @procedure.schema
|
@@ -39,586 +38,584 @@ module PLSQL
|
|
39
38
|
|
40
39
|
private
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
41
|
+
def get_overload_from_arguments_list(args)
|
42
|
+
# if not overloaded then overload index 0 is used
|
43
|
+
return 0 unless @procedure.overloaded?
|
44
|
+
# If named arguments are used then
|
45
|
+
# there should be just one Hash argument with symbol keys
|
46
|
+
if args.size == 1 && args[0].is_a?(Hash) && args[0].keys.all? { |k| k.is_a?(Symbol) }
|
47
|
+
args_keys = args[0].keys
|
48
|
+
# implicit SELF argument for object instance procedures
|
49
|
+
args_keys << :self if @self && !args_keys.include?(:self)
|
50
|
+
number_of_args = args_keys.size
|
51
|
+
matching_overloads = [] # overloads with exact or smaller number of matching named arguments
|
52
|
+
overload_argument_list.keys.each do |ov|
|
53
|
+
# assume that missing arguments have default value
|
54
|
+
missing_arguments_count = overload_argument_list[ov].size - number_of_args
|
55
|
+
if missing_arguments_count >= 0 &&
|
56
|
+
args_keys.all? { |k| overload_argument_list[ov].include?(k) }
|
57
|
+
matching_overloads << [ov, missing_arguments_count]
|
58
|
+
end
|
59
59
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
60
|
+
# pick first matching overload with smallest missing arguments count
|
61
|
+
# (hoping that missing arguments will be defaulted - cannot find default value from all_arguments)
|
62
|
+
overload = matching_overloads.sort_by { |ov, score| score }[0][0]
|
63
|
+
# otherwise try matching by sequential arguments count and types
|
64
|
+
else
|
65
|
+
number_of_args = args.size
|
66
|
+
matching_types = []
|
67
|
+
# if implicit SELF argument for object instance procedures should be passed
|
68
|
+
# then it should be added as first argument to find matches
|
69
|
+
if @self
|
70
|
+
number_of_args += 1
|
71
|
+
matching_types << ["OBJECT"]
|
72
|
+
end
|
73
|
+
args.each do |arg|
|
74
|
+
matching_types << matching_oracle_types_for_ruby_value(arg)
|
75
|
+
end
|
76
|
+
exact_overloads = [] # overloads with exact number of matching arguments
|
77
|
+
smaller_overloads = [] # overloads with smaller number of matching arguments
|
78
|
+
# overload = overload_argument_list.keys.detect do |ov|
|
79
|
+
# overload_argument_list[ov].size == number_of_args
|
80
|
+
# end
|
81
|
+
overload_argument_list.keys.each do |ov|
|
82
|
+
score = 0 # lower score is better match
|
83
|
+
ov_arg_list_size = overload_argument_list[ov].size
|
84
|
+
if (number_of_args <= ov_arg_list_size &&
|
85
|
+
(0..(number_of_args - 1)).all? do |i|
|
86
|
+
ov_arg = overload_argument_list[ov][i]
|
87
|
+
matching_types[i] == :all || # either value matches any type
|
88
|
+
(ind = matching_types[i].index(overload_arguments[ov][ov_arg][:data_type])) &&
|
89
|
+
(score += ind) # or add index of matched type
|
90
|
+
end)
|
91
|
+
if number_of_args == ov_arg_list_size
|
92
|
+
exact_overloads << [ov, score]
|
93
|
+
else
|
94
|
+
smaller_overloads << [ov, score]
|
95
|
+
end
|
96
96
|
end
|
97
97
|
end
|
98
|
+
# pick either first exact matching overload of first matching with smaller argument count
|
99
|
+
# (hoping that missing arguments will be defaulted - cannot find default value from all_arguments)
|
100
|
+
overload = if !exact_overloads.empty?
|
101
|
+
exact_overloads.sort_by { |ov, score| score }[0][0]
|
102
|
+
elsif !smaller_overloads.empty?
|
103
|
+
smaller_overloads.sort_by { |ov, score| score }[0][0]
|
104
|
+
end
|
98
105
|
end
|
99
|
-
|
100
|
-
|
101
|
-
overload = if !exact_overloads.empty?
|
102
|
-
exact_overloads.sort_by{|ov, score| score}[0][0]
|
103
|
-
elsif !smaller_overloads.empty?
|
104
|
-
smaller_overloads.sort_by{|ov, score| score}[0][0]
|
105
|
-
end
|
106
|
+
raise ArgumentError, "Wrong number or types of arguments passed to overloaded PL/SQL procedure" unless overload
|
107
|
+
overload
|
106
108
|
end
|
107
|
-
raise ArgumentError, "Wrong number or types of arguments passed to overloaded PL/SQL procedure" unless overload
|
108
|
-
overload
|
109
|
-
end
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
110
|
+
MATCHING_TYPES = {
|
111
|
+
integer: ["NUMBER", "NATURAL", "NATURALN", "POSITIVE", "POSITIVEN", "SIGNTYPE", "SIMPLE_INTEGER", "PLS_INTEGER", "BINARY_INTEGER"],
|
112
|
+
decimal: ["NUMBER", "BINARY_FLOAT", "BINARY_DOUBLE"],
|
113
|
+
string: ["VARCHAR", "VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR", "CLOB", "BLOB", "XMLTYPE"],
|
114
|
+
date: ["DATE"],
|
115
|
+
time: ["DATE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"],
|
116
|
+
boolean: ["PL/SQL BOOLEAN"],
|
117
|
+
hash: ["PL/SQL RECORD", "OBJECT", "PL/SQL TABLE"],
|
118
|
+
array: ["TABLE", "VARRAY"],
|
119
|
+
cursor: ["REF CURSOR"]
|
120
|
+
}
|
121
|
+
def matching_oracle_types_for_ruby_value(value)
|
122
|
+
case value
|
123
|
+
when NilClass
|
124
|
+
:all
|
125
|
+
when Integer
|
126
|
+
MATCHING_TYPES[:integer]
|
127
|
+
when BigDecimal, Float
|
128
|
+
MATCHING_TYPES[:decimal]
|
129
|
+
when String
|
130
|
+
MATCHING_TYPES[:string]
|
131
|
+
when Date
|
132
|
+
MATCHING_TYPES[:date]
|
133
|
+
when Time
|
134
|
+
MATCHING_TYPES[:time]
|
135
|
+
when TrueClass, FalseClass
|
136
|
+
MATCHING_TYPES[:boolean]
|
137
|
+
when Hash
|
138
|
+
MATCHING_TYPES[:hash]
|
139
|
+
when Array
|
140
|
+
MATCHING_TYPES[:array]
|
141
|
+
when CursorCommon
|
142
|
+
MATCHING_TYPES[:cursor]
|
143
|
+
end
|
144
144
|
end
|
145
|
-
end
|
146
145
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
146
|
+
def construct_sql(args)
|
147
|
+
@declare_sql = ""
|
148
|
+
@assignment_sql = ""
|
149
|
+
@call_sql = ""
|
150
|
+
@return_sql = ""
|
151
|
+
@return_vars = []
|
152
|
+
@return_vars_metadata = {}
|
153
|
+
|
154
|
+
@call_sql << add_return if return_metadata
|
155
|
+
# construct procedure call if procedure name is available
|
156
|
+
# otherwise will get surrounding call_sql from @procedure (used for table statements)
|
157
|
+
if procedure_name
|
158
|
+
@call_sql << "#{schema_name}." if schema_name
|
159
|
+
@call_sql << "#{package_name}." if package_name
|
160
|
+
@call_sql << "#{procedure_name}("
|
161
|
+
end
|
162
|
+
|
163
|
+
@bind_values = {}
|
164
|
+
@bind_metadata = {}
|
165
|
+
|
166
|
+
# Named arguments
|
167
|
+
# there should be just one Hash argument with symbol keys
|
168
|
+
if args.size == 1 && args[0].is_a?(Hash) && args[0].keys.all? { |k| k.is_a?(Symbol) } &&
|
169
|
+
# do not use named arguments if procedure has just one PL/SQL record PL/SQL table or object type argument -
|
170
|
+
# in that case passed Hash should be used as value for this PL/SQL record argument
|
171
|
+
# (which will be processed in sequential arguments bracnh)
|
172
|
+
!(argument_list.size == 1 &&
|
173
|
+
["PL/SQL RECORD", "PL/SQL TABLE", "OBJECT"].include?(arguments[(only_argument = argument_list[0])][:data_type]) &&
|
174
|
+
args[0].keys != [only_argument])
|
175
|
+
# Add missing output arguments with nil value
|
176
|
+
arguments.each do |arg, metadata|
|
177
|
+
if !args[0].has_key?(arg) && metadata[:in_out] == "OUT"
|
178
|
+
args[0][arg] = nil
|
179
|
+
end
|
180
180
|
end
|
181
|
+
# Add SELF argument if provided
|
182
|
+
args[0][:self] = @self if @self
|
183
|
+
# Add passed parameters to procedure call with parameter names
|
184
|
+
@call_sql << args[0].map do |arg, value|
|
185
|
+
"#{arg} => " << add_argument(arg, value)
|
186
|
+
end.join(", ")
|
187
|
+
|
188
|
+
# Sequential arguments
|
189
|
+
else
|
190
|
+
# add SELF as first argument if provided
|
191
|
+
args.unshift(@self) if @self
|
192
|
+
argument_count = argument_list.size
|
193
|
+
raise ArgumentError, "Too many arguments passed to PL/SQL procedure" if args.size > argument_count
|
194
|
+
# Add missing output arguments with nil value
|
195
|
+
if args.size < argument_count &&
|
196
|
+
(args.size...argument_count).all? { |i| arguments[argument_list[i]][:in_out] == "OUT" }
|
197
|
+
args += [nil] * (argument_count - args.size)
|
198
|
+
end
|
199
|
+
# Add passed parameters to procedure call in sequence
|
200
|
+
@call_sql << (0...args.size).map do |i|
|
201
|
+
arg = argument_list[i]
|
202
|
+
value = args[i]
|
203
|
+
add_argument(arg, value)
|
204
|
+
end.join(", ")
|
181
205
|
end
|
182
|
-
|
183
|
-
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
# Sequential arguments
|
190
|
-
else
|
191
|
-
# add SELF as first argument if provided
|
192
|
-
args.unshift(@self) if @self
|
193
|
-
argument_count = argument_list.size
|
194
|
-
raise ArgumentError, "Too many arguments passed to PL/SQL procedure" if args.size > argument_count
|
195
|
-
# Add missing output arguments with nil value
|
196
|
-
if args.size < argument_count &&
|
197
|
-
(args.size...argument_count).all?{|i| arguments[argument_list[i]][:in_out] == 'OUT'}
|
198
|
-
args += [nil] * (argument_count - args.size)
|
206
|
+
|
207
|
+
# finish procedure call construction if procedure name is available
|
208
|
+
# otherwise will get surrounding call_sql from @procedure (used for table statements)
|
209
|
+
if procedure_name
|
210
|
+
@call_sql << ");\n"
|
211
|
+
else
|
212
|
+
@call_sql = @procedure.call_sql(@call_sql)
|
199
213
|
end
|
200
|
-
|
201
|
-
@call_sql << (0...args.size).map do |i|
|
202
|
-
arg = argument_list[i]
|
203
|
-
value = args[i]
|
204
|
-
add_argument(arg, value)
|
205
|
-
end.join(', ')
|
206
|
-
end
|
207
|
-
|
208
|
-
# finish procedure call construction if procedure name is available
|
209
|
-
# otherwise will get surrounding call_sql from @procedure (used for table statements)
|
210
|
-
if procedure_name
|
211
|
-
@call_sql << ");\n"
|
212
|
-
else
|
213
|
-
@call_sql = @procedure.call_sql(@call_sql)
|
214
|
-
end
|
215
|
-
add_out_variables
|
214
|
+
add_out_variables
|
216
215
|
|
217
|
-
|
218
|
-
|
219
|
-
|
216
|
+
@sql = @declare_sql.empty? ? "" : "DECLARE\n" << @declare_sql
|
217
|
+
@sql << "BEGIN\n" << @assignment_sql << dbms_output_enable_sql << @call_sql << @return_sql << "END;\n"
|
218
|
+
end
|
220
219
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
"l_#{argument}"
|
233
|
-
when 'PL/SQL BOOLEAN'
|
234
|
-
@declare_sql << "l_#{argument} BOOLEAN;\n"
|
235
|
-
@assignment_sql << "l_#{argument} := (:#{argument} = 1);\n"
|
236
|
-
@bind_values[argument] = value.nil? ? nil : (value ? 1 : 0)
|
237
|
-
@bind_metadata[argument] = argument_metadata.merge(:data_type => "NUMBER", :data_precision => 1)
|
238
|
-
"l_#{argument}"
|
239
|
-
when 'UNDEFINED'
|
240
|
-
if argument_metadata[:type_name] == 'XMLTYPE'
|
241
|
-
@declare_sql << "l_#{argument} XMLTYPE;\n"
|
242
|
-
@assignment_sql << "l_#{argument} := XMLTYPE(:#{argument});\n" if not value.nil?
|
243
|
-
@bind_values[argument] = value if not value.nil?
|
244
|
-
@bind_metadata[argument] = argument_metadata.merge(:data_type => "CLOB")
|
220
|
+
def add_argument(argument, value, argument_metadata = nil)
|
221
|
+
argument_metadata ||= arguments[argument]
|
222
|
+
raise ArgumentError, "Wrong argument #{argument.inspect} passed to PL/SQL procedure" unless argument_metadata
|
223
|
+
case argument_metadata[:data_type]
|
224
|
+
when "PL/SQL RECORD"
|
225
|
+
add_record_declaration(argument, argument_metadata)
|
226
|
+
record_assignment_sql, record_bind_values, record_bind_metadata =
|
227
|
+
record_assignment_sql_values_metadata(argument, argument_metadata, value)
|
228
|
+
@assignment_sql << record_assignment_sql
|
229
|
+
@bind_values.merge!(record_bind_values)
|
230
|
+
@bind_metadata.merge!(record_bind_metadata)
|
245
231
|
"l_#{argument}"
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
insert_values_into_tmp_table(argument, argument_metadata, value)
|
232
|
+
when "PL/SQL BOOLEAN"
|
233
|
+
@declare_sql << "l_#{argument} BOOLEAN;\n"
|
234
|
+
@assignment_sql << "l_#{argument} := (:#{argument} = 1);\n"
|
235
|
+
@bind_values[argument] = value.nil? ? nil : (value ? 1 : 0)
|
236
|
+
@bind_metadata[argument] = argument_metadata.merge(data_type: "NUMBER", data_precision: 1)
|
252
237
|
"l_#{argument}"
|
238
|
+
when "UNDEFINED"
|
239
|
+
if argument_metadata[:type_name] == "XMLTYPE"
|
240
|
+
@declare_sql << "l_#{argument} XMLTYPE;\n"
|
241
|
+
@assignment_sql << "l_#{argument} := XMLTYPE(:#{argument});\n" if not value.nil?
|
242
|
+
@bind_values[argument] = value if not value.nil?
|
243
|
+
@bind_metadata[argument] = argument_metadata.merge(data_type: "CLOB")
|
244
|
+
"l_#{argument}"
|
245
|
+
end
|
253
246
|
else
|
254
|
-
|
255
|
-
|
256
|
-
|
247
|
+
# TABLE or PL/SQL TABLE type defined inside package
|
248
|
+
if argument_metadata[:tmp_table_name]
|
249
|
+
add_table_declaration_and_assignment(argument, argument_metadata)
|
250
|
+
insert_values_into_tmp_table(argument, argument_metadata, value)
|
251
|
+
"l_#{argument}"
|
252
|
+
else
|
253
|
+
@bind_values[argument] = value
|
254
|
+
@bind_metadata[argument] = argument_metadata
|
255
|
+
":#{argument}"
|
256
|
+
end
|
257
257
|
end
|
258
258
|
end
|
259
|
-
end
|
260
259
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
260
|
+
def add_table_declaration_and_assignment(argument, argument_metadata)
|
261
|
+
is_index_by_table = argument_metadata[:data_type] == "PL/SQL TABLE"
|
262
|
+
@declare_sql << "l_#{argument} #{argument_metadata[:sql_type_name]}#{is_index_by_table ? nil : " := #{argument_metadata[:sql_type_name]}()"};\n"
|
263
|
+
@assignment_sql << "FOR r_#{argument} IN c_#{argument} LOOP\n"
|
264
|
+
@assignment_sql << "l_#{argument}.EXTEND;\n" unless is_index_by_table
|
265
|
+
case argument_metadata[:element][:data_type]
|
266
|
+
when "PL/SQL RECORD"
|
267
|
+
fields = record_fields_sorted_by_position(argument_metadata[:element][:fields])
|
268
|
+
fields_string = is_index_by_table ? "*" : fields.join(", ")
|
269
|
+
@declare_sql << "CURSOR c_#{argument} IS SELECT #{fields_string} FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
|
270
|
+
if is_index_by_table
|
271
|
+
fields.each do |field|
|
272
|
+
@assignment_sql << "l_#{argument}(r_#{argument}.i__).#{field} := r_#{argument}.#{field};\n"
|
273
|
+
end
|
274
|
+
else
|
275
|
+
@assignment_sql << "l_#{argument}(l_#{argument}.COUNT) := r_#{argument};\n"
|
274
276
|
end
|
275
277
|
else
|
276
|
-
@
|
278
|
+
@declare_sql << "CURSOR c_#{argument} IS SELECT * FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
|
279
|
+
@assignment_sql << "l_#{argument}(r_#{argument}.i__) := r_#{argument}.element;\n"
|
277
280
|
end
|
278
|
-
|
279
|
-
@
|
280
|
-
@assignment_sql << "l_#{argument}(r_#{argument}.i__) := r_#{argument}.element;\n"
|
281
|
+
@assignment_sql << "END LOOP;\n"
|
282
|
+
@assignment_sql << "DELETE FROM #{argument_metadata[:tmp_table_name]};\n"
|
281
283
|
end
|
282
|
-
@assignment_sql << "END LOOP;\n"
|
283
|
-
@assignment_sql << "DELETE FROM #{argument_metadata[:tmp_table_name]};\n"
|
284
|
-
end
|
285
284
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
if is_index_by_table
|
290
|
-
raise ArgumentError, "Hash value should be passed for #{argument.inspect} argument" unless values.is_a?(Hash)
|
291
|
-
else
|
292
|
-
raise ArgumentError, "Array value should be passed for #{argument.inspect} argument" unless values.is_a?(Array)
|
293
|
-
end
|
294
|
-
tmp_table = @schema.root_schema.send(argument_metadata[:tmp_table_name])
|
295
|
-
# insert values without autocommit
|
296
|
-
old_autocommit = @schema.connection.autocommit?
|
297
|
-
@schema.connection.autocommit = false if old_autocommit
|
298
|
-
tmp_table.delete
|
299
|
-
case argument_metadata[:element][:data_type]
|
300
|
-
when 'PL/SQL RECORD'
|
301
|
-
values_with_index = []
|
285
|
+
def insert_values_into_tmp_table(argument, argument_metadata, values)
|
286
|
+
return unless values && !values.empty?
|
287
|
+
is_index_by_table = argument_metadata[:data_type] == "PL/SQL TABLE"
|
302
288
|
if is_index_by_table
|
303
|
-
|
289
|
+
raise ArgumentError, "Hash value should be passed for #{argument.inspect} argument" unless values.is_a?(Hash)
|
304
290
|
else
|
305
|
-
|
291
|
+
raise ArgumentError, "Array value should be passed for #{argument.inspect} argument" unless values.is_a?(Array)
|
306
292
|
end
|
307
|
-
tmp_table.
|
308
|
-
|
309
|
-
|
310
|
-
if
|
311
|
-
|
293
|
+
tmp_table = @schema.root_schema.send(argument_metadata[:tmp_table_name])
|
294
|
+
# insert values without autocommit
|
295
|
+
old_autocommit = @schema.connection.autocommit?
|
296
|
+
@schema.connection.autocommit = false if old_autocommit
|
297
|
+
tmp_table.delete
|
298
|
+
case argument_metadata[:element][:data_type]
|
299
|
+
when "PL/SQL RECORD"
|
300
|
+
values_with_index = []
|
301
|
+
if is_index_by_table
|
302
|
+
values.each { |i, v| values_with_index << v.merge(i__: i) }
|
303
|
+
else
|
304
|
+
values.each_with_index { |v, i| values_with_index << v.merge(i__: i + 1) }
|
305
|
+
end
|
306
|
+
tmp_table.insert values_with_index
|
312
307
|
else
|
313
|
-
|
308
|
+
values_with_index = []
|
309
|
+
if is_index_by_table
|
310
|
+
values.each { |i, v| values_with_index << [v, i] }
|
311
|
+
else
|
312
|
+
values.each_with_index { |v, i| values_with_index << [v, i + 1] }
|
313
|
+
end
|
314
|
+
tmp_table.insert_values [:element, :i__], *values_with_index
|
314
315
|
end
|
315
|
-
|
316
|
+
@schema.connection.autocommit = true if old_autocommit
|
316
317
|
end
|
317
|
-
@schema.connection.autocommit = true if old_autocommit
|
318
|
-
end
|
319
318
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
319
|
+
def add_record_declaration(argument, argument_metadata)
|
320
|
+
@declare_sql << if argument_metadata[:type_subname]
|
321
|
+
"l_#{argument} #{argument_metadata[:sql_type_name]};\n"
|
322
|
+
else
|
323
|
+
fields_metadata = argument_metadata[:fields]
|
324
|
+
sql = "TYPE t_#{argument} IS RECORD (\n"
|
325
|
+
sql << record_fields_sorted_by_position(fields_metadata).map do |field|
|
326
|
+
metadata = fields_metadata[field]
|
327
|
+
"#{field} #{type_to_sql(metadata)}"
|
328
|
+
end.join(",\n")
|
329
|
+
sql << ");\n"
|
330
|
+
sql << "l_#{argument} t_#{argument};\n"
|
331
|
+
end
|
332
332
|
end
|
333
|
-
end
|
334
333
|
|
335
|
-
|
336
|
-
|
337
|
-
|
334
|
+
def record_fields_sorted_by_position(fields_metadata)
|
335
|
+
fields_metadata.keys.sort_by { |k| fields_metadata[k][:position] }
|
336
|
+
end
|
338
337
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
338
|
+
def record_assignment_sql_values_metadata(argument, argument_metadata, record_value)
|
339
|
+
sql = ""
|
340
|
+
bind_values = {}
|
341
|
+
bind_metadata = {}
|
342
|
+
(record_value || {}).each do |key, value|
|
343
|
+
field = key.is_a?(Symbol) ? key : key.to_s.downcase.to_sym
|
344
|
+
metadata = argument_metadata[:fields][field]
|
345
|
+
raise ArgumentError, "Wrong field name #{key.inspect} passed to PL/SQL record argument #{argument.inspect}" unless metadata
|
346
|
+
bind_variable = :"#{argument}_f#{metadata[:position]}"
|
347
|
+
case metadata[:data_type]
|
348
|
+
when "PL/SQL BOOLEAN"
|
349
|
+
sql << "l_#{argument}.#{field} := (:#{bind_variable} = 1);\n"
|
350
|
+
bind_values[bind_variable] = value.nil? ? nil : (value ? 1 : 0)
|
351
|
+
bind_metadata[bind_variable] = metadata.merge(data_type: "NUMBER", data_precision: 1)
|
352
|
+
else
|
353
|
+
sql << "l_#{argument}.#{field} := :#{bind_variable};\n"
|
354
|
+
bind_values[bind_variable] = value
|
355
|
+
bind_metadata[bind_variable] = metadata
|
356
|
+
end
|
357
357
|
end
|
358
|
+
[sql, bind_values, bind_metadata]
|
358
359
|
end
|
359
|
-
[sql, bind_values, bind_metadata]
|
360
|
-
end
|
361
360
|
|
362
|
-
|
363
|
-
|
364
|
-
|
361
|
+
def add_return
|
362
|
+
add_return_variable(:return, return_metadata, true)
|
363
|
+
end
|
365
364
|
|
366
|
-
|
367
|
-
|
368
|
-
|
365
|
+
def add_out_variables
|
366
|
+
out_list.each do |argument|
|
367
|
+
add_return_variable(argument, arguments[argument])
|
368
|
+
end
|
369
369
|
end
|
370
|
-
end
|
371
370
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
371
|
+
def add_return_variable(argument, argument_metadata, is_return_value = false)
|
372
|
+
case argument_metadata[:data_type]
|
373
|
+
when "PL/SQL RECORD"
|
374
|
+
add_record_declaration(argument, argument_metadata) if is_return_value
|
375
|
+
argument_metadata[:fields].each do |field, metadata|
|
376
|
+
# should use different output bind variable as JDBC does not support
|
377
|
+
# if output bind variable appears in several places
|
378
|
+
bind_variable = :"#{argument}_o#{metadata[:position]}"
|
379
|
+
case metadata[:data_type]
|
380
|
+
when "PL/SQL BOOLEAN"
|
381
|
+
@return_vars << bind_variable
|
382
|
+
@return_vars_metadata[bind_variable] = metadata.merge(data_type: "NUMBER", data_precision: 1)
|
383
|
+
arg_field = "l_#{argument}.#{field}"
|
384
|
+
@return_sql << ":#{bind_variable} := " << "CASE WHEN #{arg_field} = true THEN 1 " <<
|
385
|
+
"WHEN #{arg_field} = false THEN 0 ELSE NULL END;\n"
|
386
|
+
else
|
387
|
+
@return_vars << bind_variable
|
388
|
+
@return_vars_metadata[bind_variable] = metadata
|
389
|
+
@return_sql << ":#{bind_variable} := l_#{argument}.#{field};\n"
|
390
|
+
end
|
391
|
+
end
|
392
|
+
"l_#{argument} := " if is_return_value
|
393
|
+
when "UNDEFINED"
|
394
|
+
if argument_metadata[:type_name] == "XMLTYPE"
|
395
|
+
@declare_sql << "l_#{argument} XMLTYPE;\n" if is_return_value
|
396
|
+
bind_variable = :"o_#{argument}"
|
388
397
|
@return_vars << bind_variable
|
389
|
-
@return_vars_metadata[bind_variable] =
|
390
|
-
@return_sql << ":#{bind_variable} := l_#{argument}
|
398
|
+
@return_vars_metadata[bind_variable] = argument_metadata.merge(data_type: "CLOB")
|
399
|
+
@return_sql << ":#{bind_variable} := CASE WHEN l_#{argument} IS NOT NULL THEN l_#{argument}.getclobval() END;\n"
|
400
|
+
"l_#{argument} := " if is_return_value
|
391
401
|
end
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
402
|
+
when "PL/SQL BOOLEAN"
|
403
|
+
@declare_sql << "l_#{argument} BOOLEAN;\n" if is_return_value
|
404
|
+
@declare_sql << "o_#{argument} NUMBER(1);\n"
|
405
|
+
# should use different output bind variable as JDBC does not support
|
406
|
+
# if output bind variable appears in several places
|
397
407
|
bind_variable = :"o_#{argument}"
|
398
408
|
@return_vars << bind_variable
|
399
|
-
@return_vars_metadata[bind_variable] = argument_metadata.merge(:
|
400
|
-
@return_sql << "
|
409
|
+
@return_vars_metadata[bind_variable] = argument_metadata.merge(data_type: "NUMBER", data_precision: 1)
|
410
|
+
@return_sql << "IF l_#{argument} IS NULL THEN\no_#{argument} := NULL;\n" <<
|
411
|
+
"ELSIF l_#{argument} THEN\no_#{argument} := 1;\nELSE\no_#{argument} := 0;\nEND IF;\n" <<
|
412
|
+
":#{bind_variable} := o_#{argument};\n"
|
401
413
|
"l_#{argument} := " if is_return_value
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
@return_vars_metadata[bind_variable] = argument_metadata.merge(:data_type => "NUMBER", :data_precision => 1)
|
411
|
-
@return_sql << "IF l_#{argument} IS NULL THEN\no_#{argument} := NULL;\n" <<
|
412
|
-
"ELSIF l_#{argument} THEN\no_#{argument} := 1;\nELSE\no_#{argument} := 0;\nEND IF;\n" <<
|
413
|
-
":#{bind_variable} := o_#{argument};\n"
|
414
|
-
"l_#{argument} := " if is_return_value
|
415
|
-
else
|
416
|
-
if argument_metadata[:tmp_table_name]
|
417
|
-
add_return_table(argument, argument_metadata, is_return_value)
|
418
|
-
elsif is_return_value
|
419
|
-
@return_vars << argument
|
420
|
-
@return_vars_metadata[argument] = argument_metadata
|
421
|
-
":#{argument} := "
|
414
|
+
else
|
415
|
+
if argument_metadata[:tmp_table_name]
|
416
|
+
add_return_table(argument, argument_metadata, is_return_value)
|
417
|
+
elsif is_return_value
|
418
|
+
@return_vars << argument
|
419
|
+
@return_vars_metadata[argument] = argument_metadata
|
420
|
+
":#{argument} := "
|
421
|
+
end
|
422
422
|
end
|
423
423
|
end
|
424
|
-
end
|
425
424
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
425
|
+
def add_return_table(argument, argument_metadata, is_return_value = false)
|
426
|
+
is_index_by_table = argument_metadata[:data_type] == "PL/SQL TABLE"
|
427
|
+
declare_i__
|
428
|
+
@declare_sql << "l_return #{return_metadata[:sql_type_name]};\n" if is_return_value
|
429
|
+
@return_vars << argument
|
430
|
+
@return_vars_metadata[argument] = argument_metadata.merge(data_type: "REF CURSOR")
|
431
|
+
@return_sql << if is_index_by_table
|
432
|
+
"i__ := l_#{argument}.FIRST;\nLOOP\nEXIT WHEN i__ IS NULL;\n"
|
433
|
+
else
|
434
|
+
"IF l_#{argument}.COUNT > 0 THEN\nFOR i__ IN l_#{argument}.FIRST..l_#{argument}.LAST LOOP\n"
|
435
|
+
end
|
436
|
+
case argument_metadata[:element][:data_type]
|
437
|
+
when "PL/SQL RECORD"
|
438
|
+
field_names = record_fields_sorted_by_position(argument_metadata[:element][:fields])
|
439
|
+
values_string = field_names.map { |f| "l_#{argument}(i__).#{f}" }.join(", ")
|
440
|
+
@return_sql << "INSERT INTO #{argument_metadata[:tmp_table_name]} VALUES (#{values_string}, i__);\n"
|
441
|
+
return_fields_string = is_index_by_table ? "*" : field_names.join(", ")
|
442
|
+
else
|
443
|
+
@return_sql << "INSERT INTO #{argument_metadata[:tmp_table_name]} VALUES (l_#{argument}(i__), i__);\n"
|
444
|
+
return_fields_string = "*"
|
445
|
+
end
|
446
|
+
@return_sql << "i__ := l_#{argument}.NEXT(i__);\n" if is_index_by_table
|
447
|
+
@return_sql << "END LOOP;\n"
|
448
|
+
@return_sql << "END IF;\n" unless is_index_by_table
|
449
|
+
@return_sql << "OPEN :#{argument} FOR SELECT #{return_fields_string} FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
|
450
|
+
@return_sql << "DELETE FROM #{argument_metadata[:tmp_table_name]};\n"
|
451
|
+
"l_#{argument} := " if is_return_value
|
452
|
+
end
|
454
453
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
454
|
+
# declare once temp variable i__ that is used as itertor
|
455
|
+
def declare_i__
|
456
|
+
unless @declared_i__
|
457
|
+
@declare_sql << "i__ PLS_INTEGER;\n"
|
458
|
+
@declared_i__ = true
|
459
|
+
end
|
460
460
|
end
|
461
|
-
end
|
462
461
|
|
463
|
-
|
464
|
-
|
465
|
-
|
462
|
+
def type_to_sql(metadata)
|
463
|
+
ProcedureCommon.type_to_sql(metadata)
|
464
|
+
end
|
466
465
|
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
466
|
+
def get_return_value
|
467
|
+
# if function with output parameters
|
468
|
+
if return_metadata && out_list.size > 0
|
469
|
+
result = [function_return_value, {}]
|
470
|
+
out_list.each do |k|
|
471
|
+
result[1][k] = out_variable_value(k)
|
472
|
+
end
|
473
|
+
result
|
474
|
+
# if function without output parameters
|
475
|
+
elsif return_metadata
|
476
|
+
function_return_value
|
477
|
+
# if procedure with output parameters
|
478
|
+
elsif out_list.size > 0
|
479
|
+
result = {}
|
480
|
+
out_list.each do |k|
|
481
|
+
result[k] = out_variable_value(k)
|
482
|
+
end
|
483
|
+
result
|
484
|
+
# if procedure without output parameters
|
485
|
+
else
|
486
|
+
nil
|
483
487
|
end
|
484
|
-
result
|
485
|
-
# if procedure without output parameters
|
486
|
-
else
|
487
|
-
nil
|
488
488
|
end
|
489
|
-
end
|
490
489
|
|
491
|
-
|
492
|
-
|
493
|
-
|
490
|
+
def function_return_value
|
491
|
+
return_variable_value(:return, return_metadata)
|
492
|
+
end
|
494
493
|
|
495
|
-
|
496
|
-
|
497
|
-
|
494
|
+
def out_variable_value(argument)
|
495
|
+
return_variable_value(argument, arguments[argument])
|
496
|
+
end
|
498
497
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
else
|
509
|
-
return_value[field] = field_value
|
510
|
-
end
|
511
|
-
end
|
512
|
-
return_value
|
513
|
-
when 'PL/SQL BOOLEAN'
|
514
|
-
numeric_value = @cursor[":o_#{argument}"]
|
515
|
-
numeric_value.nil? ? nil : numeric_value == 1
|
516
|
-
when 'UNDEFINED'
|
517
|
-
if argument_metadata[:type_name] == 'XMLTYPE'
|
518
|
-
@cursor[":o_#{argument}"]
|
519
|
-
end
|
520
|
-
else
|
521
|
-
if argument_metadata[:tmp_table_name]
|
522
|
-
is_index_by_table = argument_metadata[:data_type] == 'PL/SQL TABLE'
|
523
|
-
case argument_metadata[:element][:data_type]
|
524
|
-
when 'PL/SQL RECORD'
|
525
|
-
if is_index_by_table
|
526
|
-
Hash[*@cursor[":#{argument}"].fetch_hash_all.map{|row| [row.delete(:i__), row]}.flatten]
|
498
|
+
def return_variable_value(argument, argument_metadata)
|
499
|
+
case argument_metadata[:data_type]
|
500
|
+
when "PL/SQL RECORD"
|
501
|
+
return_value = {}
|
502
|
+
argument_metadata[:fields].each do |field, metadata|
|
503
|
+
field_value = @cursor[":#{argument}_o#{metadata[:position]}"]
|
504
|
+
case metadata[:data_type]
|
505
|
+
when "PL/SQL BOOLEAN"
|
506
|
+
return_value[field] = field_value.nil? ? nil : field_value == 1
|
527
507
|
else
|
528
|
-
|
508
|
+
return_value[field] = field_value
|
529
509
|
end
|
530
|
-
|
531
|
-
|
532
|
-
|
510
|
+
end
|
511
|
+
return_value
|
512
|
+
when "PL/SQL BOOLEAN"
|
513
|
+
numeric_value = @cursor[":o_#{argument}"]
|
514
|
+
numeric_value.nil? ? nil : numeric_value == 1
|
515
|
+
when "UNDEFINED"
|
516
|
+
if argument_metadata[:type_name] == "XMLTYPE"
|
517
|
+
@cursor[":o_#{argument}"]
|
518
|
+
end
|
519
|
+
else
|
520
|
+
if argument_metadata[:tmp_table_name]
|
521
|
+
is_index_by_table = argument_metadata[:data_type] == "PL/SQL TABLE"
|
522
|
+
case argument_metadata[:element][:data_type]
|
523
|
+
when "PL/SQL RECORD"
|
524
|
+
if is_index_by_table
|
525
|
+
Hash[*@cursor[":#{argument}"].fetch_hash_all.map { |row| [row.delete(:i__), row] }.flatten]
|
526
|
+
else
|
527
|
+
@cursor[":#{argument}"].fetch_hash_all
|
528
|
+
end
|
533
529
|
else
|
534
|
-
|
530
|
+
if is_index_by_table
|
531
|
+
Hash[*@cursor[":#{argument}"].fetch_all.map { |row| [row[1], row[0]] }.flatten]
|
532
|
+
else
|
533
|
+
@cursor[":#{argument}"].fetch_all.map { |row| row[0] }
|
534
|
+
end
|
535
535
|
end
|
536
|
+
else
|
537
|
+
@cursor[":#{argument}"]
|
536
538
|
end
|
537
|
-
else
|
538
|
-
@cursor[":#{argument}"]
|
539
539
|
end
|
540
540
|
end
|
541
|
-
end
|
542
541
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
542
|
+
def overload_argument_list
|
543
|
+
@overload_argument_list ||=
|
544
|
+
@skip_self ? @procedure.argument_list_without_self : @procedure.argument_list
|
545
|
+
end
|
547
546
|
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
547
|
+
def overload_arguments
|
548
|
+
@overload_arguments ||=
|
549
|
+
@skip_self ? @procedure.arguments_without_self : @procedure.arguments
|
550
|
+
end
|
552
551
|
|
553
|
-
|
554
|
-
|
555
|
-
|
552
|
+
def argument_list
|
553
|
+
@argument_list ||= overload_argument_list[@overload]
|
554
|
+
end
|
556
555
|
|
557
|
-
|
558
|
-
|
559
|
-
|
556
|
+
def arguments
|
557
|
+
@arguments ||= overload_arguments[@overload]
|
558
|
+
end
|
560
559
|
|
561
|
-
|
562
|
-
|
563
|
-
|
560
|
+
def return_metadata
|
561
|
+
@return_metadata ||= @procedure.return[@overload]
|
562
|
+
end
|
564
563
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
564
|
+
def out_list
|
565
|
+
@out_list ||=
|
566
|
+
@skip_self ? @procedure.out_list_without_self[@overload] : @procedure.out_list[@overload]
|
567
|
+
end
|
569
568
|
|
570
|
-
|
571
|
-
|
572
|
-
|
569
|
+
def schema_name
|
570
|
+
@schema_name ||= @procedure.schema_name
|
571
|
+
end
|
573
572
|
|
574
|
-
|
575
|
-
|
576
|
-
|
573
|
+
def package_name
|
574
|
+
@package_name ||= @procedure.package
|
575
|
+
end
|
577
576
|
|
578
|
-
|
579
|
-
|
580
|
-
|
577
|
+
def procedure_name
|
578
|
+
@procedure_name ||= @procedure.procedure
|
579
|
+
end
|
581
580
|
|
582
|
-
|
583
|
-
|
584
|
-
|
581
|
+
def dbms_output_enable_sql
|
582
|
+
@dbms_output_stream ? "DBMS_OUTPUT.ENABLE(#{@schema.dbms_output_buffer_size});\n" : ""
|
583
|
+
end
|
585
584
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
cursor.exec
|
598
|
-
lines = cursor[':dbms_output_lines']
|
599
|
-
cursor.close
|
600
|
-
else
|
601
|
-
cursor = @schema.connection.parse("BEGIN sys.dbms_output.get_line(:line, :status); END;")
|
602
|
-
while true do
|
603
|
-
cursor.bind_param(':line', nil, :data_type => 'VARCHAR2', :in_out => 'OUT')
|
604
|
-
cursor.bind_param(':status', nil, :data_type => 'NUMBER', :in_out => 'OUT')
|
585
|
+
def dbms_output_lines
|
586
|
+
lines = []
|
587
|
+
if @dbms_output_stream
|
588
|
+
if (@schema.connection.database_version <=> [10, 2, 0, 0]) >= 0
|
589
|
+
cursor = @schema.connection.parse("BEGIN DBMS_OUTPUT.GET_LINES(:dbms_output_lines, :dbms_output_numlines); END;\n")
|
590
|
+
cursor.bind_param(":dbms_output_lines", nil,
|
591
|
+
data_type: "TABLE",
|
592
|
+
data_length: nil,
|
593
|
+
sql_type_name: "SYS.DBMSOUTPUT_LINESARRAY",
|
594
|
+
in_out: "OUT")
|
595
|
+
cursor.bind_param(":dbms_output_numlines", Schema::DBMS_OUTPUT_MAX_LINES, data_type: "NUMBER", in_out: "IN/OUT")
|
605
596
|
cursor.exec
|
606
|
-
|
607
|
-
|
597
|
+
lines = cursor[":dbms_output_lines"]
|
598
|
+
cursor.close
|
599
|
+
else
|
600
|
+
cursor = @schema.connection.parse("BEGIN sys.dbms_output.get_line(:line, :status); END;")
|
601
|
+
while true do
|
602
|
+
cursor.bind_param(":line", nil, data_type: "VARCHAR2", in_out: "OUT")
|
603
|
+
cursor.bind_param(":status", nil, data_type: "NUMBER", in_out: "OUT")
|
604
|
+
cursor.exec
|
605
|
+
break unless cursor[":status"] == 0
|
606
|
+
lines << cursor[":line"]
|
607
|
+
end
|
608
|
+
cursor.close
|
608
609
|
end
|
609
|
-
cursor.close
|
610
610
|
end
|
611
|
+
lines
|
611
612
|
end
|
612
|
-
lines
|
613
|
-
end
|
614
613
|
|
615
|
-
|
616
|
-
|
617
|
-
|
614
|
+
def dbms_output_log
|
615
|
+
dbms_output_lines.each do |line|
|
616
|
+
@dbms_output_stream.puts "DBMS_OUTPUT: #{line}" if line
|
617
|
+
end
|
618
|
+
@dbms_output_stream.flush if @dbms_output_stream
|
618
619
|
end
|
619
|
-
@dbms_output_stream.flush if @dbms_output_stream
|
620
|
-
end
|
621
|
-
|
622
620
|
end
|
623
|
-
|
624
621
|
end
|