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