ruby-plsql 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +30 -0
  3. data/.github/stale.yml +37 -0
  4. data/.rubocop.yml +153 -0
  5. data/.travis.yml +20 -6
  6. data/.travis/oracle/download.sh +9 -10
  7. data/.travis/oracle/install.sh +6 -6
  8. data/Gemfile +13 -9
  9. data/History.txt +26 -0
  10. data/README.md +9 -5
  11. data/Rakefile +31 -26
  12. data/VERSION +1 -1
  13. data/Vagrantfile +2 -2
  14. data/gemfiles/Gemfile.activerecord-5.0 +21 -0
  15. data/gemfiles/Gemfile.activerecord-5.1 +21 -0
  16. data/gemfiles/Gemfile.activerecord-5.2 +21 -0
  17. data/lib/plsql/connection.rb +16 -18
  18. data/lib/plsql/helpers.rb +1 -3
  19. data/lib/plsql/jdbc_connection.rb +66 -61
  20. data/lib/plsql/oci8_patches.rb +2 -2
  21. data/lib/plsql/oci_connection.rb +51 -69
  22. data/lib/plsql/package.rb +5 -8
  23. data/lib/plsql/procedure.rb +75 -78
  24. data/lib/plsql/procedure_call.rb +498 -501
  25. data/lib/plsql/schema.rb +95 -100
  26. data/lib/plsql/sequence.rb +10 -13
  27. data/lib/plsql/sql_statements.rb +9 -11
  28. data/lib/plsql/table.rb +59 -63
  29. data/lib/plsql/type.rb +71 -76
  30. data/lib/plsql/variable.rb +89 -94
  31. data/lib/plsql/version.rb +1 -1
  32. data/lib/plsql/view.rb +16 -19
  33. data/ruby-plsql.gemspec +41 -37
  34. data/spec/plsql/connection_spec.rb +67 -67
  35. data/spec/plsql/package_spec.rb +15 -15
  36. data/spec/plsql/procedure_spec.rb +286 -233
  37. data/spec/plsql/schema_spec.rb +22 -23
  38. data/spec/plsql/sequence_spec.rb +2 -2
  39. data/spec/plsql/sql_statements_spec.rb +5 -5
  40. data/spec/plsql/table_spec.rb +77 -77
  41. data/spec/plsql/type_spec.rb +23 -29
  42. data/spec/plsql/variable_spec.rb +59 -59
  43. data/spec/plsql/version_spec.rb +4 -4
  44. data/spec/plsql/view_spec.rb +42 -42
  45. data/spec/spec_helper.rb +37 -29
  46. data/spec/support/test_db.rb +12 -13
  47. metadata +44 -26
  48. data/.travis/oracle/LICENSE +0 -5
  49. data/.travis/oracle/README.md +0 -64
  50. data/.travis/oracle/download.js +0 -100
@@ -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
- def get_overload_from_arguments_list(args)
43
- # if not overloaded then overload index 0 is used
44
- return 0 unless @procedure.overloaded?
45
- # If named arguments are used then
46
- # there should be just one Hash argument with symbol keys
47
- if args.size == 1 && args[0].is_a?(Hash) && args[0].keys.all?{|k| k.is_a?(Symbol)}
48
- args_keys = args[0].keys
49
- # implicit SELF argument for object instance procedures
50
- args_keys << :self if @self && !args_keys.include?(:self)
51
- number_of_args = args_keys.size
52
- matching_overloads = [] # overloads with exact or smaller number of matching named arguments
53
- overload_argument_list.keys.each do |ov|
54
- # assume that missing arguments have default value
55
- missing_arguments_count = overload_argument_list[ov].size - number_of_args
56
- if missing_arguments_count >= 0 &&
57
- args_keys.all?{|k| overload_argument_list[ov].include?(k)}
58
- matching_overloads << [ov, missing_arguments_count]
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
- end
61
- # pick first matching overload with smallest missing arguments count
62
- # (hoping that missing arguments will be defaulted - cannot find default value from all_arguments)
63
- overload = matching_overloads.sort_by{|ov, score| score}[0][0]
64
- # otherwise try matching by sequential arguments count and types
65
- else
66
- number_of_args = args.size
67
- matching_types = []
68
- # if implicit SELF argument for object instance procedures should be passed
69
- # then it should be added as first argument to find matches
70
- if @self
71
- number_of_args += 1
72
- matching_types << ['OBJECT']
73
- end
74
- args.each do |arg|
75
- matching_types << matching_oracle_types_for_ruby_value(arg)
76
- end
77
- exact_overloads = [] # overloads with exact number of matching arguments
78
- smaller_overloads = [] # overloads with smaller number of matching arguments
79
- # overload = overload_argument_list.keys.detect do |ov|
80
- # overload_argument_list[ov].size == number_of_args
81
- # end
82
- overload_argument_list.keys.each do |ov|
83
- score = 0 # lower score is better match
84
- ov_arg_list_size = overload_argument_list[ov].size
85
- if (number_of_args <= ov_arg_list_size &&
86
- (0..(number_of_args-1)).all? do |i|
87
- ov_arg = overload_argument_list[ov][i]
88
- matching_types[i] == :all || # either value matches any type
89
- (ind = matching_types[i].index(overload_arguments[ov][ov_arg][:data_type])) &&
90
- (score += ind) # or add index of matched type
91
- end)
92
- if number_of_args == ov_arg_list_size
93
- exact_overloads << [ov, score]
94
- else
95
- smaller_overloads << [ov, score]
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
- # pick either first exact matching overload of first matching with smaller argument count
100
- # (hoping that missing arguments will be defaulted - cannot find default value from all_arguments)
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
- MATCHING_TYPES = {
112
- :integer => ['NUMBER', 'NATURAL', 'NATURALN', 'POSITIVE', 'POSITIVEN', 'SIGNTYPE', 'SIMPLE_INTEGER', 'PLS_INTEGER', 'BINARY_INTEGER'],
113
- :decimal => ['NUMBER', 'BINARY_FLOAT', 'BINARY_DOUBLE'],
114
- :string => ['VARCHAR', 'VARCHAR2', 'NVARCHAR2', 'CHAR', 'NCHAR', 'CLOB', 'BLOB', 'XMLTYPE'],
115
- :date => ['DATE'],
116
- :time => ['DATE', 'TIMESTAMP', 'TIMESTAMP WITH TIME ZONE', 'TIMESTAMP WITH LOCAL TIME ZONE'],
117
- :boolean => ['PL/SQL BOOLEAN'],
118
- :hash => ['PL/SQL RECORD', 'OBJECT', 'PL/SQL TABLE'],
119
- :array => ['TABLE', 'VARRAY'],
120
- :cursor => ['REF CURSOR']
121
- }
122
- def matching_oracle_types_for_ruby_value(value)
123
- case value
124
- when NilClass
125
- :all
126
- when Fixnum, Bignum
127
- MATCHING_TYPES[:integer]
128
- when BigDecimal, Float
129
- MATCHING_TYPES[:decimal]
130
- when String
131
- MATCHING_TYPES[:string]
132
- when Date
133
- MATCHING_TYPES[:date]
134
- when Time
135
- MATCHING_TYPES[:time]
136
- when TrueClass, FalseClass
137
- MATCHING_TYPES[:boolean]
138
- when Hash
139
- MATCHING_TYPES[:hash]
140
- when Array
141
- MATCHING_TYPES[:array]
142
- when CursorCommon
143
- MATCHING_TYPES[:cursor]
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
- def construct_sql(args)
148
- @declare_sql = ""
149
- @assignment_sql = ""
150
- @call_sql = ""
151
- @return_sql = ""
152
- @return_vars = []
153
- @return_vars_metadata = {}
154
-
155
- @call_sql << add_return if return_metadata
156
- # construct procedure call if procedure name is available
157
- # otherwise will get surrounding call_sql from @procedure (used for table statements)
158
- if procedure_name
159
- @call_sql << "#{schema_name}." if schema_name
160
- @call_sql << "#{package_name}." if package_name
161
- @call_sql << "#{procedure_name}("
162
- end
163
-
164
- @bind_values = {}
165
- @bind_metadata = {}
166
-
167
- # Named arguments
168
- # there should be just one Hash argument with symbol keys
169
- if args.size == 1 && args[0].is_a?(Hash) && args[0].keys.all?{|k| k.is_a?(Symbol)} &&
170
- # do not use named arguments if procedure has just one PL/SQL record PL/SQL table or object type argument -
171
- # in that case passed Hash should be used as value for this PL/SQL record argument
172
- # (which will be processed in sequential arguments bracnh)
173
- !(argument_list.size == 1 &&
174
- ['PL/SQL RECORD','PL/SQL TABLE','OBJECT'].include?(arguments[(only_argument=argument_list[0])][:data_type]) &&
175
- args[0].keys != [only_argument])
176
- # Add missing output arguments with nil value
177
- arguments.each do |arg, metadata|
178
- if !args[0].has_key?(arg) && metadata[:in_out] == 'OUT'
179
- args[0][arg] = nil
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
- # Add SELF argument if provided
183
- args[0][:self] = @self if @self
184
- # Add passed parameters to procedure call with parameter names
185
- @call_sql << args[0].map do |arg, value|
186
- "#{arg} => " << add_argument(arg, value)
187
- end.join(', ')
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
- # Add passed parameters to procedure call in sequence
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
- @sql = @declare_sql.empty? ? "" : "DECLARE\n" << @declare_sql
218
- @sql << "BEGIN\n" << @assignment_sql << dbms_output_enable_sql << @call_sql << @return_sql << "END;\n"
219
- end
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
- def add_argument(argument, value, argument_metadata=nil)
222
- argument_metadata ||= arguments[argument]
223
- raise ArgumentError, "Wrong argument #{argument.inspect} passed to PL/SQL procedure" unless argument_metadata
224
- case argument_metadata[:data_type]
225
- when 'PL/SQL RECORD'
226
- add_record_declaration(argument, argument_metadata)
227
- record_assignment_sql, record_bind_values, record_bind_metadata =
228
- record_assignment_sql_values_metadata(argument, argument_metadata, value)
229
- @assignment_sql << record_assignment_sql
230
- @bind_values.merge!(record_bind_values)
231
- @bind_metadata.merge!(record_bind_metadata)
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
- end
247
- else
248
- # TABLE or PL/SQL TABLE type defined inside package
249
- if argument_metadata[:tmp_table_name]
250
- add_table_declaration_and_assignment(argument, argument_metadata)
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
- @bind_values[argument] = value
255
- @bind_metadata[argument] = argument_metadata
256
- ":#{argument}"
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
- def add_table_declaration_and_assignment(argument, argument_metadata)
262
- is_index_by_table = argument_metadata[:data_type] == 'PL/SQL TABLE'
263
- @declare_sql << "l_#{argument} #{argument_metadata[:sql_type_name]}#{is_index_by_table ? nil : " := #{argument_metadata[:sql_type_name]}()"};\n"
264
- @assignment_sql << "FOR r_#{argument} IN c_#{argument} LOOP\n"
265
- @assignment_sql << "l_#{argument}.EXTEND;\n" unless is_index_by_table
266
- case argument_metadata[:element][:data_type]
267
- when 'PL/SQL RECORD'
268
- fields = record_fields_sorted_by_position(argument_metadata[:element][:fields])
269
- fields_string = is_index_by_table ? "*" : fields.join(', ')
270
- @declare_sql << "CURSOR c_#{argument} IS SELECT #{fields_string} FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
271
- if is_index_by_table
272
- fields.each do |field|
273
- @assignment_sql << "l_#{argument}(r_#{argument}.i__).#{field} := r_#{argument}.#{field};\n"
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
- @assignment_sql << "l_#{argument}(l_#{argument}.COUNT) := r_#{argument};\n"
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
- else
279
- @declare_sql << "CURSOR c_#{argument} IS SELECT * FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
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
- def insert_values_into_tmp_table(argument, argument_metadata, values)
287
- return unless values && !values.empty?
288
- is_index_by_table = argument_metadata[:data_type] == 'PL/SQL TABLE'
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
- values.each{|i,v| values_with_index << v.merge(:i__ => i)}
289
+ raise ArgumentError, "Hash value should be passed for #{argument.inspect} argument" unless values.is_a?(Hash)
304
290
  else
305
- values.each_with_index{|v,i| values_with_index << v.merge(:i__ => i+1)}
291
+ raise ArgumentError, "Array value should be passed for #{argument.inspect} argument" unless values.is_a?(Array)
306
292
  end
307
- tmp_table.insert values_with_index
308
- else
309
- values_with_index = []
310
- if is_index_by_table
311
- values.each{|i,v| values_with_index << [v, i]}
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
- values.each_with_index{|v,i| values_with_index << [v, i+1]}
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
- tmp_table.insert_values [:element, :i__], *values_with_index
316
+ @schema.connection.autocommit = true if old_autocommit
316
317
  end
317
- @schema.connection.autocommit = true if old_autocommit
318
- end
319
318
 
320
- def add_record_declaration(argument, argument_metadata)
321
- @declare_sql << if argument_metadata[:type_subname]
322
- "l_#{argument} #{argument_metadata[:sql_type_name]};\n"
323
- else
324
- fields_metadata = argument_metadata[:fields]
325
- sql = "TYPE t_#{argument} IS RECORD (\n"
326
- sql << record_fields_sorted_by_position(fields_metadata).map do |field|
327
- metadata = fields_metadata[field]
328
- "#{field} #{type_to_sql(metadata)}"
329
- end.join(",\n")
330
- sql << ");\n"
331
- sql << "l_#{argument} t_#{argument};\n"
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
- def record_fields_sorted_by_position(fields_metadata)
336
- fields_metadata.keys.sort_by{|k| fields_metadata[k][:position]}
337
- end
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
- def record_assignment_sql_values_metadata(argument, argument_metadata, record_value)
340
- sql = ""
341
- bind_values = {}
342
- bind_metadata = {}
343
- (record_value||{}).each do |key, value|
344
- field = key.is_a?(Symbol) ? key : key.to_s.downcase.to_sym
345
- metadata = argument_metadata[:fields][field]
346
- raise ArgumentError, "Wrong field name #{key.inspect} passed to PL/SQL record argument #{argument.inspect}" unless metadata
347
- bind_variable = :"#{argument}_f#{metadata[:position]}"
348
- case metadata[:data_type]
349
- when 'PL/SQL BOOLEAN'
350
- sql << "l_#{argument}.#{field} := (:#{bind_variable} = 1);\n"
351
- bind_values[bind_variable] = value.nil? ? nil : (value ? 1 : 0)
352
- bind_metadata[bind_variable] = metadata.merge(:data_type => "NUMBER", :data_precision => 1)
353
- else
354
- sql << "l_#{argument}.#{field} := :#{bind_variable};\n"
355
- bind_values[bind_variable] = value
356
- bind_metadata[bind_variable] = metadata
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
- def add_return
363
- add_return_variable(:return, return_metadata, true)
364
- end
361
+ def add_return
362
+ add_return_variable(:return, return_metadata, true)
363
+ end
365
364
 
366
- def add_out_variables
367
- out_list.each do |argument|
368
- add_return_variable(argument, arguments[argument])
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
- def add_return_variable(argument, argument_metadata, is_return_value=false)
373
- case argument_metadata[:data_type]
374
- when 'PL/SQL RECORD'
375
- add_record_declaration(argument, argument_metadata) if is_return_value
376
- argument_metadata[:fields].each do |field, metadata|
377
- # should use different output bind variable as JDBC does not support
378
- # if output bind variable appears in several places
379
- bind_variable = :"#{argument}_o#{metadata[:position]}"
380
- case metadata[:data_type]
381
- when 'PL/SQL BOOLEAN'
382
- @return_vars << bind_variable
383
- @return_vars_metadata[bind_variable] = metadata.merge(:data_type => "NUMBER", :data_precision => 1)
384
- arg_field = "l_#{argument}.#{field}"
385
- @return_sql << ":#{bind_variable} := " << "CASE WHEN #{arg_field} = true THEN 1 " <<
386
- "WHEN #{arg_field} = false THEN 0 ELSE NULL END;\n"
387
- else
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] = metadata
390
- @return_sql << ":#{bind_variable} := l_#{argument}.#{field};\n"
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
- end
393
- "l_#{argument} := " if is_return_value
394
- when 'UNDEFINED'
395
- if argument_metadata[:type_name] == 'XMLTYPE'
396
- @declare_sql << "l_#{argument} XMLTYPE;\n" if is_return_value
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(:data_type => "CLOB")
400
- @return_sql << ":#{bind_variable} := CASE WHEN l_#{argument} IS NOT NULL THEN l_#{argument}.getclobval() END;\n"
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
- end
403
- when 'PL/SQL BOOLEAN'
404
- @declare_sql << "l_#{argument} BOOLEAN;\n" if is_return_value
405
- @declare_sql << "o_#{argument} NUMBER(1);\n"
406
- # should use different output bind variable as JDBC does not support
407
- # if output bind variable appears in several places
408
- bind_variable = :"o_#{argument}"
409
- @return_vars << bind_variable
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
- def add_return_table(argument, argument_metadata, is_return_value=false)
427
- is_index_by_table = argument_metadata[:data_type] == 'PL/SQL TABLE'
428
- declare_i__
429
- @declare_sql << "l_return #{return_metadata[:sql_type_name]};\n" if is_return_value
430
- @return_vars << argument
431
- @return_vars_metadata[argument] = argument_metadata.merge(:data_type => "REF CURSOR")
432
- @return_sql << if is_index_by_table
433
- "i__ := l_#{argument}.FIRST;\nLOOP\nEXIT WHEN i__ IS NULL;\n"
434
- else
435
- "IF l_#{argument}.COUNT > 0 THEN\nFOR i__ IN l_#{argument}.FIRST..l_#{argument}.LAST LOOP\n"
436
- end
437
- case argument_metadata[:element][:data_type]
438
- when 'PL/SQL RECORD'
439
- field_names = record_fields_sorted_by_position(argument_metadata[:element][:fields])
440
- values_string = field_names.map{|f| "l_#{argument}(i__).#{f}"}.join(', ')
441
- @return_sql << "INSERT INTO #{argument_metadata[:tmp_table_name]} VALUES (#{values_string}, i__);\n"
442
- return_fields_string = is_index_by_table ? '*' : field_names.join(', ')
443
- else
444
- @return_sql << "INSERT INTO #{argument_metadata[:tmp_table_name]} VALUES (l_#{argument}(i__), i__);\n"
445
- return_fields_string = '*'
446
- end
447
- @return_sql << "i__ := l_#{argument}.NEXT(i__);\n" if is_index_by_table
448
- @return_sql << "END LOOP;\n"
449
- @return_sql << "END IF;\n" unless is_index_by_table
450
- @return_sql << "OPEN :#{argument} FOR SELECT #{return_fields_string} FROM #{argument_metadata[:tmp_table_name]} ORDER BY i__;\n"
451
- @return_sql << "DELETE FROM #{argument_metadata[:tmp_table_name]};\n"
452
- "l_#{argument} := " if is_return_value
453
- end
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
- # declare once temp variable i__ that is used as itertor
456
- def declare_i__
457
- unless @declared_i__
458
- @declare_sql << "i__ PLS_INTEGER;\n"
459
- @declared_i__ = true
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
- def type_to_sql(metadata)
464
- ProcedureCommon.type_to_sql(metadata)
465
- end
462
+ def type_to_sql(metadata)
463
+ ProcedureCommon.type_to_sql(metadata)
464
+ end
466
465
 
467
- def get_return_value
468
- # if function with output parameters
469
- if return_metadata && out_list.size > 0
470
- result = [function_return_value, {}]
471
- out_list.each do |k|
472
- result[1][k] = out_variable_value(k)
473
- end
474
- result
475
- # if function without output parameters
476
- elsif return_metadata
477
- function_return_value
478
- # if procedure with output parameters
479
- elsif out_list.size > 0
480
- result = {}
481
- out_list.each do |k|
482
- result[k] = out_variable_value(k)
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
- def function_return_value
492
- return_variable_value(:return, return_metadata)
493
- end
490
+ def function_return_value
491
+ return_variable_value(:return, return_metadata)
492
+ end
494
493
 
495
- def out_variable_value(argument)
496
- return_variable_value(argument, arguments[argument])
497
- end
494
+ def out_variable_value(argument)
495
+ return_variable_value(argument, arguments[argument])
496
+ end
498
497
 
499
- def return_variable_value(argument, argument_metadata)
500
- case argument_metadata[:data_type]
501
- when 'PL/SQL RECORD'
502
- return_value = {}
503
- argument_metadata[:fields].each do |field, metadata|
504
- field_value = @cursor[":#{argument}_o#{metadata[:position]}"]
505
- case metadata[:data_type]
506
- when 'PL/SQL BOOLEAN'
507
- return_value[field] = field_value.nil? ? nil : field_value == 1
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
- @cursor[":#{argument}"].fetch_hash_all
508
+ return_value[field] = field_value
529
509
  end
530
- else
531
- if is_index_by_table
532
- Hash[*@cursor[":#{argument}"].fetch_all.map{|row| [row[1], row[0]]}.flatten]
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
- @cursor[":#{argument}"].fetch_all.map{|row| row[0]}
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
- def overload_argument_list
544
- @overload_argument_list ||=
545
- @skip_self ? @procedure.argument_list_without_self : @procedure.argument_list
546
- end
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
- def overload_arguments
549
- @overload_arguments ||=
550
- @skip_self ? @procedure.arguments_without_self : @procedure.arguments
551
- end
547
+ def overload_arguments
548
+ @overload_arguments ||=
549
+ @skip_self ? @procedure.arguments_without_self : @procedure.arguments
550
+ end
552
551
 
553
- def argument_list
554
- @argument_list ||= overload_argument_list[@overload]
555
- end
552
+ def argument_list
553
+ @argument_list ||= overload_argument_list[@overload]
554
+ end
556
555
 
557
- def arguments
558
- @arguments ||= overload_arguments[@overload]
559
- end
556
+ def arguments
557
+ @arguments ||= overload_arguments[@overload]
558
+ end
560
559
 
561
- def return_metadata
562
- @return_metadata ||= @procedure.return[@overload]
563
- end
560
+ def return_metadata
561
+ @return_metadata ||= @procedure.return[@overload]
562
+ end
564
563
 
565
- def out_list
566
- @out_list ||=
567
- @skip_self ? @procedure.out_list_without_self[@overload] : @procedure.out_list[@overload]
568
- end
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
- def schema_name
571
- @schema_name ||= @procedure.schema_name
572
- end
569
+ def schema_name
570
+ @schema_name ||= @procedure.schema_name
571
+ end
573
572
 
574
- def package_name
575
- @package_name ||= @procedure.package
576
- end
573
+ def package_name
574
+ @package_name ||= @procedure.package
575
+ end
577
576
 
578
- def procedure_name
579
- @procedure_name ||= @procedure.procedure
580
- end
577
+ def procedure_name
578
+ @procedure_name ||= @procedure.procedure
579
+ end
581
580
 
582
- def dbms_output_enable_sql
583
- @dbms_output_stream ? "DBMS_OUTPUT.ENABLE(#{@schema.dbms_output_buffer_size});\n" : ""
584
- end
581
+ def dbms_output_enable_sql
582
+ @dbms_output_stream ? "DBMS_OUTPUT.ENABLE(#{@schema.dbms_output_buffer_size});\n" : ""
583
+ end
585
584
 
586
- def dbms_output_lines
587
- lines = []
588
- if @dbms_output_stream
589
- if (@schema.connection.database_version <=> [10, 2, 0, 0]) >= 0
590
- cursor = @schema.connection.parse("BEGIN DBMS_OUTPUT.GET_LINES(:dbms_output_lines, :dbms_output_numlines); END;\n")
591
- cursor.bind_param(':dbms_output_lines', nil,
592
- :data_type => 'TABLE',
593
- :data_length => nil,
594
- :sql_type_name => "SYS.DBMSOUTPUT_LINESARRAY",
595
- :in_out => 'OUT')
596
- cursor.bind_param(':dbms_output_numlines', Schema::DBMS_OUTPUT_MAX_LINES, :data_type => 'NUMBER', :in_out => 'IN/OUT')
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
- break unless cursor[':status'] == 0
607
- lines << cursor[':line']
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
- def dbms_output_log
616
- dbms_output_lines.each do |line|
617
- @dbms_output_stream.puts "DBMS_OUTPUT: #{line}" if line
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