ruby-plsql 0.5.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/.github/stale.yml +37 -0
  3. data/.github/workflows/rubocop.yml +37 -0
  4. data/.github/workflows/test.yml +69 -0
  5. data/.rubocop.yml +147 -0
  6. data/.travis.yml +88 -0
  7. data/.travis/oracle/download.sh +15 -0
  8. data/.travis/oracle/install.sh +32 -0
  9. data/.travis/setup_accounts.sh +9 -0
  10. data/Gemfile +17 -9
  11. data/History.txt +76 -0
  12. data/README.md +29 -6
  13. data/Rakefile +31 -26
  14. data/VERSION +1 -1
  15. data/Vagrantfile +4 -4
  16. data/ci/network/admin/tnsnames.ora +7 -0
  17. data/ci/setup_accounts.sh +9 -0
  18. data/gemfiles/Gemfile.activerecord-5.0 +21 -0
  19. data/gemfiles/Gemfile.activerecord-5.1 +21 -0
  20. data/gemfiles/Gemfile.activerecord-5.2 +21 -0
  21. data/gemfiles/Gemfile.activerecord-6.0 +21 -0
  22. data/gemfiles/Gemfile.activerecord-6.1 +21 -0
  23. data/gemfiles/Gemfile.activerecord-main +21 -0
  24. data/lib/plsql/connection.rb +19 -22
  25. data/lib/plsql/helpers.rb +1 -3
  26. data/lib/plsql/jdbc_connection.rb +70 -68
  27. data/lib/plsql/oci8_patches.rb +2 -2
  28. data/lib/plsql/oci_connection.rb +62 -77
  29. data/lib/plsql/package.rb +61 -46
  30. data/lib/plsql/procedure.rb +358 -78
  31. data/lib/plsql/procedure_call.rb +508 -463
  32. data/lib/plsql/schema.rb +96 -101
  33. data/lib/plsql/sequence.rb +10 -13
  34. data/lib/plsql/sql_statements.rb +9 -11
  35. data/lib/plsql/table.rb +60 -63
  36. data/lib/plsql/type.rb +71 -76
  37. data/lib/plsql/variable.rb +90 -94
  38. data/lib/plsql/version.rb +1 -1
  39. data/lib/plsql/view.rb +16 -19
  40. data/ruby-plsql.gemspec +55 -35
  41. data/spec/plsql/connection_spec.rb +72 -66
  42. data/spec/plsql/package_spec.rb +63 -14
  43. data/spec/plsql/procedure_spec.rb +603 -261
  44. data/spec/plsql/schema_spec.rb +47 -23
  45. data/spec/plsql/sequence_spec.rb +2 -2
  46. data/spec/plsql/sql_statements_spec.rb +6 -6
  47. data/spec/plsql/table_spec.rb +84 -79
  48. data/spec/plsql/type_spec.rb +24 -30
  49. data/spec/plsql/variable_spec.rb +80 -88
  50. data/spec/plsql/version_spec.rb +4 -4
  51. data/spec/plsql/view_spec.rb +42 -42
  52. data/spec/spec_helper.rb +38 -35
  53. data/spec/support/create_arunit_user.sql +2 -0
  54. data/spec/support/custom_config.rb.sample +14 -0
  55. data/spec/support/test_db.rb +12 -13
  56. data/spec/support/unlock_and_setup_hr_user.sql +2 -0
  57. metadata +111 -34
@@ -18,17 +18,16 @@ require "plsql/oci8_patches"
18
18
 
19
19
  # check ruby-oci8 version
20
20
  required_oci8_version = [2, 0, 3]
21
- oci8_version_ints = OCI8::VERSION.scan(/\d+/).map{|s| s.to_i}
21
+ oci8_version_ints = OCI8::VERSION.scan(/\d+/).map { |s| s.to_i }
22
22
  if (oci8_version_ints <=> required_oci8_version) < 0
23
23
  raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
24
24
  end
25
25
 
26
26
  module PLSQL
27
27
  class OCIConnection < Connection #:nodoc:
28
-
29
28
  def self.create_raw(params)
30
29
  connection_string = if params[:host]
31
- "//#{params[:host]}:#{params[:port]||1521}/#{params[:database]}"
30
+ "//#{params[:host]}:#{params[:port] || 1521}/#{params[:database]}"
32
31
  else
33
32
  params[:database]
34
33
  end
@@ -47,7 +46,7 @@ module PLSQL
47
46
  def rollback
48
47
  raw_connection.rollback
49
48
  end
50
-
49
+
51
50
  def autocommit?
52
51
  raw_connection.autocommit?
53
52
  end
@@ -68,14 +67,17 @@ module PLSQL
68
67
  class Cursor #:nodoc:
69
68
  include Connection::CursorCommon
70
69
 
71
- # stack of open cursors
72
- @@open_cursors = []
73
70
  attr_reader :raw_cursor
74
71
 
72
+ # stack of open cursors per thread
73
+ def self.open_cursors
74
+ Thread.current[:plsql_oci_cursor_stack] ||= []
75
+ end
76
+
75
77
  def initialize(conn, raw_cursor)
76
78
  @connection = conn
77
79
  @raw_cursor = raw_cursor
78
- @@open_cursors.push self
80
+ self.class.open_cursors.push self
79
81
  end
80
82
 
81
83
  def self.new_from_parse(conn, sql)
@@ -83,7 +85,7 @@ module PLSQL
83
85
  self.new(conn, raw_cursor)
84
86
  end
85
87
 
86
- def self.new_from_query(conn, sql, bindvars=[], options={})
88
+ def self.new_from_query(conn, sql, bindvars = [], options = {})
87
89
  cursor = new_from_parse(conn, sql)
88
90
  if prefetch_rows = options[:prefetch_rows]
89
91
  cursor.prefetch_rows = prefetch_rows
@@ -101,7 +103,7 @@ module PLSQL
101
103
  ora_value = @connection.ruby_value_to_ora_value(value, type)
102
104
  @raw_cursor.bind_param(arg, ora_value, type, length)
103
105
  end
104
-
106
+
105
107
  def exec(*bindvars)
106
108
  @raw_cursor.exec(*bindvars)
107
109
  end
@@ -112,11 +114,11 @@ module PLSQL
112
114
 
113
115
  def fetch
114
116
  row = @raw_cursor.fetch
115
- row && row.map{|v| @connection.ora_value_to_ruby_value(v)}
117
+ row && row.map { |v| @connection.ora_value_to_ruby_value(v) }
116
118
  end
117
119
 
118
120
  def fields
119
- @fields ||= @raw_cursor.get_col_names.map{|c| c.downcase.to_sym}
121
+ @fields ||= @raw_cursor.get_col_names.map { |c| c.downcase.to_sym }
120
122
  end
121
123
 
122
124
  def close_raw_cursor
@@ -125,38 +127,37 @@ module PLSQL
125
127
 
126
128
  def close
127
129
  # close all cursors that were created after this one
128
- while (open_cursor = @@open_cursors.pop) && !open_cursor.equal?(self)
130
+ while (open_cursor = self.class.open_cursors.pop) && !open_cursor.equal?(self)
129
131
  open_cursor.close_raw_cursor
130
132
  end
131
133
  close_raw_cursor
132
134
  end
133
-
134
135
  end
135
136
 
136
137
  def parse(sql)
137
138
  Cursor.new_from_parse(self, sql)
138
139
  end
139
140
 
140
- def cursor_from_query(sql, bindvars=[], options={})
141
+ def cursor_from_query(sql, bindvars = [], options = {})
141
142
  Cursor.new_from_query(self, sql, bindvars, options)
142
143
  end
143
144
 
144
145
  def plsql_to_ruby_data_type(metadata)
145
146
  data_type, data_length = metadata[:data_type], metadata[:data_length]
146
147
  case data_type
147
- when "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
148
+ when "VARCHAR", "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
148
149
  [String, data_length || 32767]
149
150
  when "CLOB", "NCLOB"
150
151
  [OCI8::CLOB, nil]
151
152
  when "BLOB"
152
153
  [OCI8::BLOB, nil]
153
- when "NUMBER", "PLS_INTEGER", "BINARY_INTEGER"
154
+ when "NUMBER", "NATURAL", "NATURALN", "POSITIVE", "POSITIVEN", "SIGNTYPE", "SIMPLE_INTEGER", "PLS_INTEGER", "BINARY_INTEGER"
154
155
  [OraNumber, nil]
155
156
  when "DATE"
156
157
  [DateTime, nil]
157
158
  when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
158
159
  [Time, nil]
159
- when "TABLE", "VARRAY", "OBJECT"
160
+ when "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
160
161
  # create Ruby class for collection
161
162
  klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
162
163
  unless klass
@@ -171,18 +172,16 @@ module PLSQL
171
172
  end
172
173
  end
173
174
 
174
- def ruby_value_to_ora_value(value, type=nil)
175
+ def ruby_value_to_ora_value(value, type = nil)
175
176
  type ||= value.class
176
177
  case type.to_s.to_sym
177
- when :Fixnum, :BigDecimal, :String
178
+ when :Integer, :BigDecimal, :String
178
179
  value
179
180
  when :OraNumber
180
181
  # pass parameters as OraNumber to avoid rounding errors
181
182
  case value
182
- when Bignum
183
- OraNumber.new(value.to_s)
184
183
  when BigDecimal
185
- OraNumber.new(value.to_s('F'))
184
+ OraNumber.new(value.to_s("F"))
186
185
  when TrueClass
187
186
  OraNumber.new(1)
188
187
  when FalseClass
@@ -215,7 +214,7 @@ module PLSQL
215
214
  raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
216
215
  elem_list = value.map do |elem|
217
216
  if (attr_tdo = tdo.coll_attr.typeinfo)
218
- attr_type, attr_length = plsql_to_ruby_data_type(:data_type => 'OBJECT', :sql_type_name => attr_tdo.typename)
217
+ attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr_tdo.typename)
219
218
  else
220
219
  attr_type = elem.class
221
220
  end
@@ -224,7 +223,7 @@ module PLSQL
224
223
  # construct collection value
225
224
  # TODO: change setting instance variable to appropriate ruby-oci8 method call when available
226
225
  collection = type.new(raw_oci_connection)
227
- collection.instance_variable_set('@attributes', elem_list)
226
+ collection.instance_variable_set("@attributes", elem_list)
228
227
  collection
229
228
  else # object type
230
229
  raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
@@ -234,7 +233,7 @@ module PLSQL
234
233
  case attr.datatype
235
234
  when OCI8::TDO::ATTR_NAMED_TYPE, OCI8::TDO::ATTR_NAMED_COLLECTION
236
235
  # nested object type or collection
237
- attr_type, attr_length = plsql_to_ruby_data_type(:data_type => 'OBJECT', :sql_type_name => attr.typeinfo.typename)
236
+ attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr.typeinfo.typename)
238
237
  object_attrs[key] = ruby_value_to_ora_value(object_attrs[key], attr_type)
239
238
  end
240
239
  end
@@ -263,7 +262,7 @@ module PLSQL
263
262
  when OCI8::Object::Base
264
263
  tdo = raw_oci_connection.get_tdo_by_class(value.class)
265
264
  if tdo.is_collection?
266
- value.to_ary.map{|e| ora_value_to_ruby_value(e)}
265
+ value.to_ary.map { |e| ora_value_to_ruby_value(e) }
267
266
  else # object type
268
267
  tdo.attributes.inject({}) do |hash, attr|
269
268
  hash[attr.name] = ora_value_to_ruby_value(value.instance_variable_get(:@attributes)[attr.name])
@@ -277,63 +276,49 @@ module PLSQL
277
276
  end
278
277
  end
279
278
 
280
- def describe_synonym(schema_name, synonym_name)
281
- if schema_name == 'PUBLIC'
282
- full_name = synonym_name.to_s
283
- else
284
- full_name = "#{schema_name}.#{synonym_name}"
285
- end
286
- metadata = raw_connection.describe_synonym(full_name)
287
- [metadata.schema_name, metadata.name]
288
- rescue OCIError
289
- nil
290
- end
291
-
292
279
  def database_version
293
- @database_version ||= (version = raw_connection.oracle_server_version) &&
280
+ @database_version ||= (version = raw_connection.oracle_server_version) &&
294
281
  [version.major, version.minor, version.update, version.patch]
295
282
  end
296
283
 
297
284
  private
298
-
299
- def raw_oci_connection
300
- if raw_connection.is_a? OCI8
301
- raw_connection
302
- # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
303
- # in this case we need to pass original OCI8 connection
304
- else
305
- raw_connection.instance_variable_get(:@connection)
306
- end
307
- end
308
-
309
- def ora_number_to_ruby_number(num)
310
- # return BigDecimal instead of Float to avoid rounding errors
311
- num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal.new(num.to_s))
312
- end
313
-
314
- def ora_date_to_ruby_date(val)
315
- case val
316
- when DateTime
317
- # similar implementation as in oracle_enhanced adapter
318
- begin
319
- Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.min, val.sec)
320
- rescue
321
- offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
322
- DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, offset)
323
- end
324
- when OraDate
325
- # similar implementation as in oracle_enhanced adapter
326
- begin
327
- Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.minute, val.second)
328
- rescue
329
- offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
330
- DateTime.civil(val.year, val.month, val.day, val.hour, val.minute, val.second, offset)
285
+
286
+ def raw_oci_connection
287
+ if raw_connection.is_a? OCI8
288
+ raw_connection
289
+ # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
290
+ # in this case we need to pass original OCI8 connection
291
+ else
292
+ raw_connection.instance_variable_get(:@connection)
331
293
  end
332
- else
333
- val
334
294
  end
335
- end
336
295
 
296
+ def ora_number_to_ruby_number(num)
297
+ # return BigDecimal instead of Float to avoid rounding errors
298
+ num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal(num.to_s))
299
+ end
300
+
301
+ def ora_date_to_ruby_date(val)
302
+ case val
303
+ when DateTime
304
+ # similar implementation as in oracle_enhanced adapter
305
+ begin
306
+ Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.min, val.sec)
307
+ rescue
308
+ offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
309
+ DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, offset)
310
+ end
311
+ when OraDate
312
+ # similar implementation as in oracle_enhanced adapter
313
+ begin
314
+ Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.minute, val.second)
315
+ rescue
316
+ offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
317
+ DateTime.civil(val.year, val.month, val.day, val.hour, val.minute, val.second, offset)
318
+ end
319
+ else
320
+ val
321
+ end
322
+ end
337
323
  end
338
-
339
- end
324
+ end
data/lib/plsql/package.rb CHANGED
@@ -1,29 +1,34 @@
1
1
  module PLSQL
2
-
3
2
  module PackageClassMethods #:nodoc:
4
3
  def find(schema, package)
5
- if schema.select_first(
6
- "SELECT object_name FROM all_objects
7
- WHERE owner = :owner
8
- AND object_name = :package
9
- AND object_type = 'PACKAGE'",
10
- schema.schema_name, package.to_s.upcase)
11
- new(schema, package)
12
- # search for synonym
13
- elsif (row = schema.select_first(
14
- "SELECT o.owner, o.object_name
15
- FROM all_synonyms s, all_objects o
16
- WHERE s.owner IN (:owner, 'PUBLIC')
17
- AND s.synonym_name = :synonym_name
18
- AND o.owner = s.table_owner
19
- AND o.object_name = s.table_name
20
- AND o.object_type = 'PACKAGE'
21
- ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
22
- schema.schema_name, package.to_s.upcase))
23
- new(schema, row[1], row[0])
24
- else
25
- nil
26
- end
4
+ package_name = package.to_s.upcase
5
+ find_in_schema(schema, package_name) || find_by_synonym(schema, package_name)
6
+ end
7
+
8
+ def find_in_schema(schema, package_name)
9
+ row = schema.select_first(<<-SQL, schema.schema_name, package_name)
10
+ SELECT object_name
11
+ FROM all_objects
12
+ WHERE owner = :owner
13
+ AND object_name = :package
14
+ AND object_type = 'PACKAGE'
15
+ SQL
16
+ new(schema, package_name) if row
17
+ end
18
+
19
+ def find_by_synonym(schema, package_name)
20
+ row = schema.select_first(<<-SQL, schema.schema_name, package_name)
21
+ SELECT o.object_name, o.owner
22
+ FROM all_synonyms s,
23
+ all_objects o
24
+ WHERE s.owner IN (:owner, 'PUBLIC')
25
+ AND s.synonym_name = :synonym_name
26
+ AND o.owner = s.table_owner
27
+ AND o.object_name = s.table_name
28
+ AND o.object_type = 'PACKAGE'
29
+ ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)
30
+ SQL
31
+ new(schema, row[0], row[1]) if row
27
32
  end
28
33
  end
29
34
 
@@ -41,32 +46,42 @@ module PLSQL
41
46
  PLSQL::Procedure.find(@schema, name, @package) ? true : false
42
47
  end
43
48
 
44
- private
45
-
46
- def method_missing(method, *args, &block)
47
- if assignment = (method.to_s[-1,1] == '=')
48
- method = method.to_s.chop.to_sym
49
+ def [](object_name)
50
+ object_name = object_name.to_s.downcase
51
+ @package_objects[object_name] ||= [Procedure, Variable].inject(nil) do |res, object_type|
52
+ res || object_type.find(@schema, object_name, @package, @override_schema_name)
49
53
  end
50
- object = (@package_objects[method] ||=
51
- Procedure.find(@schema, method, @package, @override_schema_name) ||
52
- Variable.find(@schema, method, @package, @override_schema_name))
53
- case object
54
- when Procedure
55
- raise ArgumentError, "Cannot assign value to package procedure '#{method.to_s.upcase}'" if assignment
56
- object.exec(*args, &block)
57
- when Variable
58
- if assignment
59
- raise ArgumentError, "Just one value can be assigned to package variable '#{method.to_s.upcase}'" unless args.size == 1 && block == nil
60
- object.value = args[0]
54
+ end
55
+
56
+ private
57
+
58
+ def method_missing(method, *args, &block)
59
+ method = method.to_s
60
+ method.chop! if (assignment = method[/=$/])
61
+
62
+ case (object = self[method])
63
+ when Procedure
64
+ if assignment
65
+ raise ArgumentError, "Cannot assign value to package procedure '#{method.upcase}'"
66
+ end
67
+ object.exec(*args, &block)
68
+ when Variable
69
+ if assignment
70
+ unless args.size == 1 && block.nil?
71
+ raise ArgumentError, "Just one value can be assigned " \
72
+ "to package variable '#{method.upcase}'"
73
+ end
74
+ object.value = args[0]
75
+ else
76
+ unless args.size == 0 && block.nil?
77
+ raise ArgumentError, "Cannot pass arguments when getting " \
78
+ "package variable '#{method.upcase}' value"
79
+ end
80
+ object.value
81
+ end
61
82
  else
62
- raise ArgumentError, "Cannot pass arguments when getting package variable '#{method.to_s.upcase}' value" unless args.size == 0 && block == nil
63
- object.value
83
+ raise ArgumentError, "No PL/SQL procedure or variable '#{method.upcase}' found"
64
84
  end
65
- else
66
- raise ArgumentError, "No PL/SQL procedure or variable '#{method.to_s.upcase}' found"
67
85
  end
68
- end
69
-
70
86
  end
71
-
72
87
  end
@@ -1,45 +1,44 @@
1
1
  module PLSQL
2
-
3
2
  module ProcedureClassMethods #:nodoc:
4
3
  def find(schema, procedure, package = nil, override_schema_name = nil)
5
4
  if package.nil?
6
5
  if (row = schema.select_first(
7
- "SELECT #{procedure_object_id_src(schema)}.object_id
8
- FROM all_procedures p, all_objects o
9
- WHERE p.owner = :owner
10
- AND p.object_name = :object_name
11
- AND o.owner = p.owner
12
- AND o.object_name = p.object_name
13
- AND o.object_type in ('PROCEDURE', 'FUNCTION')",
6
+ "SELECT #{procedure_object_id_src(schema)}.object_id
7
+ FROM all_procedures p, all_objects o
8
+ WHERE p.owner = :owner
9
+ AND p.object_name = :object_name
10
+ AND o.owner = p.owner
11
+ AND o.object_name = p.object_name
12
+ AND o.object_type in ('PROCEDURE', 'FUNCTION')",
14
13
  schema.schema_name, procedure.to_s.upcase))
15
14
  new(schema, procedure, nil, nil, row[0])
16
15
  # search for synonym
17
16
  elsif (row = schema.select_first(
18
- "SELECT o.owner, o.object_name, #{procedure_object_id_src(schema)}.object_id
19
- FROM all_synonyms s, all_objects o, all_procedures p
20
- WHERE s.owner IN (:owner, 'PUBLIC')
21
- AND s.synonym_name = :synonym_name
22
- AND o.owner = s.table_owner
23
- AND o.object_name = s.table_name
24
- AND o.object_type IN ('PROCEDURE','FUNCTION')
25
- AND o.owner = p.owner
26
- AND o.object_name = p.object_name
27
- ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
17
+ "SELECT o.owner, o.object_name, #{procedure_object_id_src(schema)}.object_id
18
+ FROM all_synonyms s, all_objects o, all_procedures p
19
+ WHERE s.owner IN (:owner, 'PUBLIC')
20
+ AND s.synonym_name = :synonym_name
21
+ AND o.owner = s.table_owner
22
+ AND o.object_name = s.table_name
23
+ AND o.object_type IN ('PROCEDURE','FUNCTION')
24
+ AND o.owner = p.owner
25
+ AND o.object_name = p.object_name
26
+ ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
28
27
  schema.schema_name, procedure.to_s.upcase))
29
28
  new(schema, row[1], nil, row[0], row[2])
30
29
  else
31
30
  nil
32
31
  end
33
32
  elsif package && (row = schema.select_first(
34
- # older Oracle versions do not have object_id column in all_procedures
35
- "SELECT #{procedure_object_id_src(schema)}.object_id
36
- FROM all_procedures p, all_objects o
37
- WHERE p.owner = :owner
38
- AND p.object_name = :object_name
39
- AND p.procedure_name = :procedure_name
40
- AND o.owner = p.owner
41
- AND o.object_name = p.object_name
42
- AND o.object_type = 'PACKAGE'",
33
+ # older Oracle versions do not have object_id column in all_procedures
34
+ "SELECT #{procedure_object_id_src(schema)}.object_id
35
+ FROM all_procedures p, all_objects o
36
+ WHERE p.owner = :owner
37
+ AND p.object_name = :object_name
38
+ AND p.procedure_name = :procedure_name
39
+ AND o.owner = p.owner
40
+ AND o.object_name = p.object_name
41
+ AND o.object_type = 'PACKAGE'",
43
42
  override_schema_name || schema.schema_name, package, procedure.to_s.upcase))
44
43
  new(schema, procedure, package, override_schema_name, row[0])
45
44
  else
@@ -49,9 +48,9 @@ module PLSQL
49
48
 
50
49
  private
51
50
 
52
- def procedure_object_id_src(schema)
53
- (schema.connection.database_version <=> [11, 1, 0, 0]) >= 0 ? "p" : "o"
54
- end
51
+ def procedure_object_id_src(schema)
52
+ (schema.connection.database_version <=> [11, 1, 0, 0]) >= 0 ? "p" : "o"
53
+ end
55
54
  end
56
55
 
57
56
  module ProcedureCommon #:nodoc:
@@ -61,21 +60,21 @@ module PLSQL
61
60
  # return type string from metadata that can be used in DECLARE block or table definition
62
61
  def self.type_to_sql(metadata) #:nodoc:
63
62
  case metadata[:data_type]
64
- when 'NUMBER'
63
+ when "NUMBER"
65
64
  precision, scale = metadata[:data_precision], metadata[:data_scale]
66
- "NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}": ""})" : ""}"
67
- when 'VARCHAR2', 'CHAR'
65
+ "NUMBER#{precision ? "(#{precision}#{scale ? ",#{scale}" : ""})" : ""}"
66
+ when "VARCHAR", "VARCHAR2", "CHAR"
68
67
  length = case metadata[:char_used]
69
- when 'C' then "#{metadata[:char_length]} CHAR"
70
- when 'B' then "#{metadata[:data_length]} BYTE"
71
- else
72
- metadata[:data_length]
68
+ when "C" then "#{metadata[:char_length]} CHAR"
69
+ when "B" then "#{metadata[:data_length]} BYTE"
70
+ else
71
+ metadata[:data_length]
73
72
  end
74
73
  "#{metadata[:data_type]}#{length && "(#{length})"}"
75
- when 'NVARCHAR2', 'NCHAR'
74
+ when "NVARCHAR2", "NCHAR"
76
75
  length = metadata[:char_length]
77
76
  "#{metadata[:data_type]}#{length && "(#{length})"}"
78
- when 'PL/SQL TABLE', 'TABLE', 'VARRAY', 'OBJECT'
77
+ when "PL/SQL TABLE", "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
79
78
  metadata[:sql_type_name]
80
79
  else
81
80
  metadata[:data_type]
@@ -84,6 +83,14 @@ module PLSQL
84
83
 
85
84
  # get procedure argument metadata from data dictionary
86
85
  def get_argument_metadata #:nodoc:
86
+ if (@schema.connection.database_version <=> [18, 0, 0, 0]) >= 0
87
+ get_argument_metadata_from_18c
88
+ else
89
+ get_argument_metadata_below_18c
90
+ end
91
+ end
92
+
93
+ def get_argument_metadata_below_18c #:nodoc:
87
94
  @arguments = {}
88
95
  @argument_list = {}
89
96
  @out_list = {}
@@ -99,12 +106,14 @@ module PLSQL
99
106
  @tmp_tables_created = {}
100
107
 
101
108
  # subprogram_id column is available just from version 10g
102
- subprogram_id_column = (@schema.connection.database_version <=> [10, 2, 0, 2]) >= 0 ? 'subprogram_id' : 'NULL'
109
+ subprogram_id_column = (@schema.connection.database_version <=> [10, 2, 0, 2]) >= 0 ? "subprogram_id" : "NULL"
110
+ # defaulted is available just from version 11g
111
+ defaulted_column = (@schema.connection.database_version <=> [11, 0, 0, 0]) >= 0 ? "defaulted" : "NULL"
103
112
 
104
113
  @schema.select_all(
105
114
  "SELECT #{subprogram_id_column}, object_name, TO_NUMBER(overload), argument_name, position, data_level,
106
115
  data_type, in_out, data_length, data_precision, data_scale, char_used,
107
- char_length, type_owner, type_name, type_subname
116
+ char_length, type_owner, type_name, type_subname, #{defaulted_column}
108
117
  FROM all_arguments
109
118
  WHERE object_id = :object_id
110
119
  AND owner = :owner
@@ -115,7 +124,7 @@ module PLSQL
115
124
 
116
125
  subprogram_id, object_name, overload, argument_name, position, data_level,
117
126
  data_type, in_out, data_length, data_precision, data_scale, char_used,
118
- char_length, type_owner, type_name, type_subname = r
127
+ char_length, type_owner, type_name, type_subname, defaulted = r
119
128
 
120
129
  @overloaded ||= !overload.nil?
121
130
  # if not overloaded then store arguments at key 0
@@ -136,7 +145,7 @@ module PLSQL
136
145
  # then generate unique ID from object_name and overload
137
146
  subprogram_id ||= "#{object_name.hash % 10000}#{overload}"
138
147
  tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
139
- elsif data_type != 'PL/SQL RECORD'
148
+ elsif data_type != "PL/SQL RECORD"
140
149
  # raise exception only when there are no overloaded procedure definitions
141
150
  # (as probably this overload will not be used at all)
142
151
  raise ArgumentError, "Parameter type #{sql_type_name} definition inside package is not supported, use CREATE TYPE outside package" if overload == 0
@@ -144,18 +153,19 @@ module PLSQL
144
153
  end
145
154
 
146
155
  argument_metadata = {
147
- :position => position && position.to_i,
148
- :data_type => data_type,
149
- :in_out => in_out,
150
- :data_length => data_length && data_length.to_i,
151
- :data_precision => data_precision && data_precision.to_i,
152
- :data_scale => data_scale && data_scale.to_i,
153
- :char_used => char_used,
154
- :char_length => char_length && char_length.to_i,
155
- :type_owner => type_owner,
156
- :type_name => type_name,
157
- :type_subname => type_subname,
158
- :sql_type_name => sql_type_name
156
+ position: position && position.to_i,
157
+ data_type: data_type,
158
+ in_out: in_out,
159
+ data_length: data_length && data_length.to_i,
160
+ data_precision: data_precision && data_precision.to_i,
161
+ data_scale: data_scale && data_scale.to_i,
162
+ char_used: char_used,
163
+ char_length: char_length && char_length.to_i,
164
+ type_owner: type_owner,
165
+ type_name: type_name,
166
+ type_subname: type_subname,
167
+ sql_type_name: sql_type_name,
168
+ defaulted: defaulted
159
169
  }
160
170
  if tmp_table_name
161
171
  @tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
@@ -163,14 +173,14 @@ module PLSQL
163
173
 
164
174
  if composite_type?(data_type)
165
175
  case data_type
166
- when 'PL/SQL RECORD'
176
+ when "PL/SQL RECORD"
167
177
  argument_metadata[:fields] = {}
168
178
  end
169
179
  previous_level_argument_metadata[data_level] = argument_metadata
170
180
  end
171
181
 
172
182
  # if function has return value
173
- if argument_name.nil? && data_level == 0 && in_out == 'OUT'
183
+ if argument_name.nil? && data_level == 0 && in_out == "OUT"
174
184
  @return[overload] = argument_metadata
175
185
  # if parameter
176
186
  else
@@ -181,9 +191,9 @@ module PLSQL
181
191
  # or lower level part of composite type
182
192
  else
183
193
  case previous_level_argument_metadata[data_level - 1][:data_type]
184
- when 'PL/SQL RECORD'
194
+ when "PL/SQL RECORD"
185
195
  previous_level_argument_metadata[data_level - 1][:fields][argument_name.downcase.to_sym] = argument_metadata
186
- when 'PL/SQL TABLE', 'TABLE', 'VARRAY', 'REF CURSOR'
196
+ when "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"
187
197
  previous_level_argument_metadata[data_level - 1][:element] = argument_metadata
188
198
  end
189
199
  end
@@ -195,11 +205,104 @@ module PLSQL
195
205
  construct_argument_list_for_overloads
196
206
  end
197
207
 
208
+ # get procedure argument metadata from data dictionary
209
+ def get_argument_metadata_from_18c #:nodoc:
210
+ @arguments = {}
211
+ @argument_list = {}
212
+ @out_list = {}
213
+ @return = {}
214
+ @overloaded = false
215
+
216
+ # store tmp tables for each overload for table parameters with types defined inside packages
217
+ @tmp_table_names = {}
218
+ # store if tmp tables are created for specific overload
219
+ @tmp_tables_created = {}
220
+
221
+ @schema.select_all(
222
+ "SELECT subprogram_id, object_name, TO_NUMBER(overload), argument_name, position,
223
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
224
+ char_length, type_owner, nvl(type_subname, type_name),
225
+ decode(type_object_type, 'PACKAGE', type_name, null), type_object_type, defaulted
226
+ FROM all_arguments
227
+ WHERE object_id = :object_id
228
+ AND owner = :owner
229
+ AND object_name = :procedure_name
230
+ ORDER BY overload, sequence",
231
+ @object_id, @schema_name, @procedure
232
+ ) do |r|
233
+
234
+ subprogram_id, object_name, overload, argument_name, position,
235
+ data_type, in_out, data_length, data_precision, data_scale, char_used,
236
+ char_length, type_owner, type_name, type_package, type_object_type, defaulted = r
237
+
238
+ @overloaded ||= !overload.nil?
239
+ # if not overloaded then store arguments at key 0
240
+ overload ||= 0
241
+ @arguments[overload] ||= {}
242
+ @return[overload] ||= nil
243
+ @tmp_table_names[overload] ||= []
244
+
245
+ sql_type_name = build_sql_type_name(type_owner, type_package, type_name)
246
+
247
+ tmp_table_name = nil
248
+ # type defined inside package
249
+ if type_package
250
+ if collection_type?(data_type)
251
+ tmp_table_name = "#{Connection::RUBY_TEMP_TABLE_PREFIX}#{@schema.connection.session_id}_#{@object_id}_#{subprogram_id}_#{position}"
252
+ end
253
+ end
254
+
255
+ argument_metadata = {
256
+ position: position && position.to_i,
257
+ data_type: data_type,
258
+ in_out: in_out,
259
+ data_length: data_length && data_length.to_i,
260
+ data_precision: data_precision && data_precision.to_i,
261
+ data_scale: data_scale && data_scale.to_i,
262
+ char_used: char_used,
263
+ char_length: char_length && char_length.to_i,
264
+ type_owner: type_owner,
265
+ type_name: type_name,
266
+ # TODO: should be renamed to type_package, when support for legacy database versions is dropped
267
+ # due to the explicit change declaration of types in oracle plsql_type-catalogs (type_package + type_name),
268
+ # the assignment of type + subtype was switched here for 18c and beyond
269
+ type_subname: type_package,
270
+ sql_type_name: sql_type_name,
271
+ defaulted: defaulted,
272
+ type_object_type: type_object_type
273
+ }
274
+ if tmp_table_name
275
+ @tmp_table_names[overload] << [(argument_metadata[:tmp_table_name] = tmp_table_name), argument_metadata]
276
+ end
277
+
278
+ if composite_type?(data_type)
279
+ case data_type
280
+ when "PL/SQL RECORD", "REF CURSOR"
281
+ argument_metadata[:fields] = get_field_definitions(argument_metadata)
282
+ when "PL/SQL TABLE", "TABLE", "VARRAY"
283
+ argument_metadata[:element] = get_element_definition(argument_metadata)
284
+ end
285
+ end
286
+
287
+ # if function has return value
288
+ if argument_name.nil? && in_out == "OUT"
289
+ @return[overload] = argument_metadata
290
+ else
291
+ # sometime there are empty IN arguments in all_arguments view for procedures without arguments (e.g. for DBMS_OUTPUT.DISABLE)
292
+ @arguments[overload][argument_name.downcase.to_sym] = argument_metadata if argument_name
293
+ end
294
+ end
295
+ # if procedure is without arguments then create default empty argument list for default overload
296
+ @arguments[0] = {} if @arguments.keys.empty?
297
+
298
+ construct_argument_list_for_overloads
299
+ end
300
+
198
301
  def construct_argument_list_for_overloads #:nodoc:
199
302
  @overloads = @arguments.keys.sort
200
303
  @overloads.each do |overload|
201
- @argument_list[overload] = @arguments[overload].keys.sort {|k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position]}
202
- @out_list[overload] = @argument_list[overload].select {|k| @arguments[overload][k][:in_out] =~ /OUT/}
304
+ @argument_list[overload] = @arguments[overload].keys.sort { |k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position] }
305
+ @out_list[overload] = @argument_list[overload].select { |k| @arguments[overload][k][:in_out] =~ /OUT/ }
203
306
  end
204
307
  end
205
308
 
@@ -207,19 +310,19 @@ module PLSQL
207
310
  return if @tmp_tables_created.nil? || @tmp_tables_created[overload]
208
311
  @tmp_table_names[overload] && @tmp_table_names[overload].each do |table_name, argument_metadata|
209
312
  sql = "CREATE GLOBAL TEMPORARY TABLE #{table_name} (\n"
210
- element_metadata = argument_metadata[:element]
211
- case element_metadata[:data_type]
212
- when 'PL/SQL RECORD'
213
- fields_metadata = element_metadata[:fields]
214
- fields_sorted_by_position = fields_metadata.keys.sort_by{|k| fields_metadata[k][:position]}
215
- sql << fields_sorted_by_position.map do |field|
216
- metadata = fields_metadata[field]
217
- "#{field} #{ProcedureCommon.type_to_sql(metadata)}"
218
- end.join(",\n")
219
- else
220
- sql << "element #{ProcedureCommon.type_to_sql(element_metadata)}"
221
- end
222
- sql << ",\ni__ NUMBER(38)\n"
313
+ element_metadata = argument_metadata[:element]
314
+ case element_metadata[:data_type]
315
+ when "PL/SQL RECORD"
316
+ fields_metadata = element_metadata[:fields]
317
+ fields_sorted_by_position = fields_metadata.keys.sort_by { |k| fields_metadata[k][:position] }
318
+ sql << fields_sorted_by_position.map do |field|
319
+ metadata = fields_metadata[field]
320
+ "#{field} #{ProcedureCommon.type_to_sql(metadata)}"
321
+ end.join(",\n")
322
+ else
323
+ sql << "element #{ProcedureCommon.type_to_sql(element_metadata)}"
324
+ end
325
+ sql << ",\ni__ NUMBER(38)\n"
223
326
  sql << ") ON COMMIT PRESERVE ROWS\n"
224
327
  sql_block = "DECLARE\nPRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\nEXECUTE IMMEDIATE :sql;\nEND;\n"
225
328
  @schema.execute sql_block, sql
@@ -227,12 +330,191 @@ module PLSQL
227
330
  @tmp_tables_created[overload] = true
228
331
  end
229
332
 
230
- PLSQL_COMPOSITE_TYPES = ['PL/SQL RECORD', 'PL/SQL TABLE', 'TABLE', 'VARRAY', 'REF CURSOR'].freeze
333
+ def build_sql_type_name(type_owner, type_package, type_name) #:nodoc:
334
+ if type_owner == nil || type_owner == "PUBLIC"
335
+ type_owner_res = ""
336
+ else
337
+ type_owner_res = "#{type_owner}."
338
+ end
339
+
340
+ if type_package == nil
341
+ type_name_res = type_name
342
+ else
343
+ type_name_res = "#{type_package}.#{type_name}"
344
+ end
345
+ type_name_res && "#{type_owner_res}#{type_name_res}"
346
+ end
347
+
348
+ def get_field_definitions(argument_metadata) #:nodoc:
349
+ fields = {}
350
+ case argument_metadata[:type_object_type]
351
+ when "PACKAGE"
352
+ @schema.select_all(
353
+ "SELECT attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, length, precision, scale, char_used
354
+ FROM ALL_PLSQL_TYPES t, ALL_PLSQL_TYPE_ATTRS ta
355
+ WHERE t.OWNER = :owner AND t.type_name = :type_name AND t.package_name = :package_name
356
+ AND ta.OWNER = t.owner AND ta.TYPE_NAME = t.TYPE_NAME AND ta.PACKAGE_NAME = t.PACKAGE_NAME
357
+ ORDER BY attr_no",
358
+ @schema_name, argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
359
+
360
+ attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, attr_length, attr_precision, attr_scale, attr_char_used = r
361
+
362
+ fields[attr_name.downcase.to_sym] = {
363
+ position: attr_no.to_i,
364
+ data_type: attr_type_owner == nil ? attr_type_name : get_composite_type(attr_type_owner, attr_type_name, attr_type_package),
365
+ in_out: argument_metadata[:in_out],
366
+ data_length: attr_length && attr_length.to_i,
367
+ data_precision: attr_precision && attr_precision.to_i,
368
+ data_scale: attr_scale && attr_scale.to_i,
369
+ char_used: attr_char_used == nil ? "0" : attr_char_used,
370
+ char_length: attr_char_used && attr_length && attr_length.to_i,
371
+ type_owner: attr_type_owner,
372
+ type_name: attr_type_owner && attr_type_name,
373
+ type_subname: attr_type_package,
374
+ sql_type_name: attr_type_owner && build_sql_type_name(attr_type_owner, attr_type_package, attr_type_name),
375
+ defaulted: argument_metadata[:defaulted]
376
+ }
377
+
378
+ if fields[attr_name.downcase.to_sym][:data_type] == "TABLE" && fields[attr_name.downcase.to_sym][:type_subname] != nil
379
+ fields[attr_name.downcase.to_sym][:fields] = get_field_definitions(fields[attr_name.downcase.to_sym])
380
+ end
381
+ end
382
+ when "TABLE", "VIEW"
383
+ @schema.select_all(
384
+ "SELECT column_id, column_name, data_type, data_length, data_precision, data_scale, char_length, char_used
385
+ FROM ALL_TAB_COLS WHERE OWNER = :owner AND TABLE_NAME = :type_name
386
+ ORDER BY column_id",
387
+ @schema_name, argument_metadata[:type_name]) do |r|
388
+
389
+ col_no, col_name, col_type_name, col_length, col_precision, col_scale, col_char_length, col_char_used = r
390
+
391
+ fields[col_name.downcase.to_sym] = {
392
+ position: col_no.to_i,
393
+ data_type: col_type_name,
394
+ in_out: argument_metadata[:in_out],
395
+ data_length: col_length && col_length.to_i,
396
+ data_precision: col_precision && col_precision.to_i,
397
+ data_scale: col_scale && col_scale.to_i,
398
+ char_used: col_char_used == nil ? "0" : col_char_used,
399
+ char_length: col_char_length && col_char_length.to_i,
400
+ type_owner: nil,
401
+ type_name: nil,
402
+ type_subname: nil,
403
+ sql_type_name: nil,
404
+ defaulted: argument_metadata[:defaulted]
405
+ }
406
+ end
407
+ end
408
+ fields
409
+ end
410
+
411
+ def get_element_definition(argument_metadata) #:nodoc:
412
+ element_metadata = {}
413
+ if collection_type?(argument_metadata[:data_type])
414
+ case argument_metadata[:type_object_type]
415
+ when "PACKAGE"
416
+ r = @schema.select_first(
417
+ "SELECT elem_type_owner, elem_type_name, elem_type_package, length, precision, scale, char_used, index_by
418
+ FROM ALL_PLSQL_COLL_TYPES t
419
+ WHERE t.OWNER = :owner AND t.TYPE_NAME = :type_name AND t.PACKAGE_NAME = :package_name",
420
+ @schema_name, argument_metadata[:type_name], argument_metadata[:type_subname])
421
+
422
+ elem_type_owner, elem_type_name, elem_type_package, elem_length, elem_precision, elem_scale, elem_char_used, index_by = r
423
+
424
+ if index_by == "VARCHAR2"
425
+ raise ArgumentError, "Index-by Varchar-Table (associative array) #{argument_metadata[:type_name]} is not supported"
426
+ end
427
+
428
+ element_metadata = {
429
+ position: 1,
430
+ data_type: if elem_type_owner == nil
431
+ elem_type_name
432
+ else
433
+ elem_type_package != nil ? "PL/SQL RECORD" : "OBJECT"
434
+ end,
435
+ in_out: argument_metadata[:in_out],
436
+ data_length: elem_length && elem_length.to_i,
437
+ data_precision: elem_precision && elem_precision.to_i,
438
+ data_scale: elem_scale && elem_scale.to_i,
439
+ char_used: elem_char_used,
440
+ char_length: elem_char_used && elem_length && elem_length.to_i,
441
+ type_owner: elem_type_owner,
442
+ type_name: elem_type_name,
443
+ type_subname: elem_type_package,
444
+ sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, elem_type_package, elem_type_name),
445
+ type_object_type: elem_type_package != nil ? "PACKAGE" : nil,
446
+ defaulted: argument_metadata[:defaulted]
447
+ }
448
+
449
+ if elem_type_package != nil
450
+ element_metadata[:fields] = get_field_definitions(element_metadata)
451
+ end
452
+ when "TYPE"
453
+ r = @schema.select_first(
454
+ "SELECT elem_type_owner, elem_type_name, length, precision, scale, char_used
455
+ FROM ALL_COLL_TYPES t
456
+ WHERE t.owner = :owner AND t.TYPE_NAME = :type_name",
457
+ @schema_name, argument_metadata[:type_name]
458
+ )
459
+ elem_type_owner, elem_type_name, elem_length, elem_precision, elem_scale, elem_char_used = r
460
+
461
+ element_metadata = {
462
+ position: 1,
463
+ data_type: elem_type_owner == nil ? elem_type_name : "OBJECT",
464
+ in_out: argument_metadata[:in_out],
465
+ data_length: elem_length && elem_length.to_i,
466
+ data_precision: elem_precision && elem_precision.to_i,
467
+ data_scale: elem_scale && elem_scale.to_i,
468
+ char_used: elem_char_used,
469
+ char_length: elem_char_used && elem_length && elem_length.to_i,
470
+ type_owner: elem_type_owner,
471
+ type_name: elem_type_name,
472
+ type_subname: nil,
473
+ sql_type_name: elem_type_owner && build_sql_type_name(elem_type_owner, nil, elem_type_name),
474
+ defaulted: argument_metadata[:defaulted]
475
+ }
476
+ end
477
+ else
478
+ element_metadata = {
479
+ position: 1,
480
+ data_type: "PL/SQL RECORD",
481
+ in_out: argument_metadata[:in_out],
482
+ data_length: nil,
483
+ data_precision: nil,
484
+ data_scale: nil,
485
+ char_used: "B",
486
+ char_length: 0,
487
+ type_owner: argument_metadata[:type_owner],
488
+ type_name: argument_metadata[:type_name],
489
+ type_subname: argument_metadata[:type_subname],
490
+ sql_type_name: build_sql_type_name(argument_metadata[:type_owner], argument_metadata[:type_subname], argument_metadata[:type_name]),
491
+ defaulted: argument_metadata[:defaulted]
492
+ }
493
+
494
+ if element_metadata[:type_subname] != nil
495
+ element_metadata[:fields] = get_field_definitions(element_metadata)
496
+ end
497
+ end
498
+ element_metadata
499
+ end
500
+
501
+ def get_composite_type(type_owner, type_name, type_package)
502
+ r = @schema.select_first("SELECT typecode FROM all_plsql_types WHERE owner = :owner AND type_name = :type_name AND package_name = :type_package
503
+ UNION ALL
504
+ SELECT typecode FROM all_types WHERE owner = :owner AND type_name = :type_name",
505
+ type_owner, type_name, type_package, type_owner, type_name)
506
+ typecode = r[0]
507
+ raise ArgumentError, "#{type_name} type #{build_sql_type_name(type_owner, type_package, type_name)} definition inside package is not supported as part of other type definition," <<
508
+ " use CREATE TYPE outside package" if typecode == "COLLECTION"
509
+ typecode
510
+ end
511
+
512
+ PLSQL_COMPOSITE_TYPES = ["PL/SQL RECORD", "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"].freeze
231
513
  def composite_type?(data_type) #:nodoc:
232
514
  PLSQL_COMPOSITE_TYPES.include? data_type
233
515
  end
234
516
 
235
- PLSQL_COLLECTION_TYPES = ['PL/SQL TABLE', 'TABLE', 'VARRAY'].freeze
517
+ PLSQL_COLLECTION_TYPES = ["PL/SQL TABLE", "TABLE", "VARRAY"].freeze
236
518
  def collection_type?(data_type) #:nodoc:
237
519
  PLSQL_COLLECTION_TYPES.include? data_type
238
520
  end
@@ -263,7 +545,5 @@ module PLSQL
263
545
  call = ProcedureCall.new(self, args)
264
546
  call.exec(&block)
265
547
  end
266
-
267
548
  end
268
-
269
549
  end