ruby-plsql 0.5.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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