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.
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