ruby-oci8 2.2.10-x64-mingw-ucrt

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +14 -0
  3. data/COPYING +30 -0
  4. data/COPYING_old +64 -0
  5. data/ChangeLog +3826 -0
  6. data/Makefile +92 -0
  7. data/NEWS +1209 -0
  8. data/README.md +66 -0
  9. data/dist-files +112 -0
  10. data/docs/bind-array-to-in_cond.md +38 -0
  11. data/docs/conflicts-local-connections-and-processes.md +98 -0
  12. data/docs/hanging-after-inactivity.md +63 -0
  13. data/docs/install-binary-package.md +44 -0
  14. data/docs/install-full-client.md +111 -0
  15. data/docs/install-instant-client.md +194 -0
  16. data/docs/install-on-osx.md +46 -0
  17. data/docs/ldap-auth-and-function-interposition.md +123 -0
  18. data/docs/number-type-mapping.md +79 -0
  19. data/docs/platform-specific-issues.md +164 -0
  20. data/docs/report-installation-issue.md +50 -0
  21. data/docs/timeout-parameters.md +94 -0
  22. data/lib/.document +1 -0
  23. data/lib/dbd/OCI8.rb +591 -0
  24. data/lib/oci8/.document +8 -0
  25. data/lib/oci8/bindtype.rb +333 -0
  26. data/lib/oci8/check_load_error.rb +146 -0
  27. data/lib/oci8/compat.rb +117 -0
  28. data/lib/oci8/connection_pool.rb +179 -0
  29. data/lib/oci8/cursor.rb +605 -0
  30. data/lib/oci8/datetime.rb +605 -0
  31. data/lib/oci8/encoding-init.rb +45 -0
  32. data/lib/oci8/encoding.yml +537 -0
  33. data/lib/oci8/metadata.rb +2148 -0
  34. data/lib/oci8/object.rb +641 -0
  35. data/lib/oci8/oci8.rb +756 -0
  36. data/lib/oci8/ocihandle.rb +591 -0
  37. data/lib/oci8/oracle_version.rb +153 -0
  38. data/lib/oci8/properties.rb +196 -0
  39. data/lib/oci8/version.rb +3 -0
  40. data/lib/oci8.rb +190 -0
  41. data/lib/oci8lib_310.so +0 -0
  42. data/lib/ruby-oci8.rb +1 -0
  43. data/metaconfig +142 -0
  44. data/pre-distclean.rb +7 -0
  45. data/ruby-oci8.gemspec +85 -0
  46. data/setup.rb +1342 -0
  47. data/test/README.md +37 -0
  48. data/test/config.rb +201 -0
  49. data/test/setup_test_object.sql +199 -0
  50. data/test/setup_test_package.sql +59 -0
  51. data/test/test_all.rb +56 -0
  52. data/test/test_appinfo.rb +62 -0
  53. data/test/test_array_dml.rb +332 -0
  54. data/test/test_bind_array.rb +70 -0
  55. data/test/test_bind_boolean.rb +99 -0
  56. data/test/test_bind_integer.rb +47 -0
  57. data/test/test_bind_raw.rb +45 -0
  58. data/test/test_bind_string.rb +105 -0
  59. data/test/test_bind_time.rb +177 -0
  60. data/test/test_break.rb +125 -0
  61. data/test/test_clob.rb +85 -0
  62. data/test/test_connection_pool.rb +124 -0
  63. data/test/test_connstr.rb +220 -0
  64. data/test/test_datetime.rb +585 -0
  65. data/test/test_dbi.rb +365 -0
  66. data/test/test_dbi_clob.rb +53 -0
  67. data/test/test_encoding.rb +103 -0
  68. data/test/test_error.rb +87 -0
  69. data/test/test_metadata.rb +2674 -0
  70. data/test/test_object.rb +546 -0
  71. data/test/test_oci8.rb +624 -0
  72. data/test/test_oracle_version.rb +68 -0
  73. data/test/test_oradate.rb +255 -0
  74. data/test/test_oranumber.rb +792 -0
  75. data/test/test_package_type.rb +981 -0
  76. data/test/test_properties.rb +17 -0
  77. data/test/test_rowid.rb +32 -0
  78. metadata +123 -0
@@ -0,0 +1,641 @@
1
+ #
2
+ # OCI8::NamedType
3
+ #
4
+ require 'oci8/metadata.rb'
5
+
6
+ class OCI8
7
+
8
+ # Returns the type descriptor object which correspond to the given class.
9
+ #
10
+ # @param [class of an OCI8::Object::Base's subclass] klass
11
+ # @return [OCI8::TDO]
12
+ #
13
+ # @private
14
+ def get_tdo_by_class(klass)
15
+ @id_to_tdo ||= {}
16
+ @name_to_tdo ||= {}
17
+ tdo = @name_to_tdo[klass.typename]
18
+ return tdo if tdo # found in the cache.
19
+
20
+ metadata = describe_any(klass.typename)
21
+ if metadata.is_a? OCI8::Metadata::Synonym
22
+ metadata = describe_any(metadata.translated_name)
23
+ end
24
+ unless metadata.is_a? OCI8::Metadata::Type
25
+ raise "unknown typename #{klass.typename}"
26
+ end
27
+ OCI8::TDO.new(self, metadata, klass)
28
+ end
29
+
30
+ # Returns the type descriptor object which correspond to the given metadata.
31
+ #
32
+ # @param [OCI8::Metadata::Base's subclass] metadata
33
+ # @return [OCI8::TDO]
34
+ #
35
+ # @private
36
+ def get_tdo_by_metadata(metadata)
37
+ @id_to_tdo ||= {}
38
+ @name_to_tdo ||= {}
39
+ tdo = @id_to_tdo[metadata.tdo_id]
40
+ return tdo if tdo
41
+
42
+ schema_name = metadata.schema_name
43
+ name = metadata.name
44
+ full_name = schema_name + '.' + name
45
+
46
+ klass = OCI8::Object::Base.get_class_by_typename(full_name)
47
+ klass = OCI8::Object::Base.get_class_by_typename(name) if klass.nil?
48
+ if klass.nil?
49
+ if schema_name == username
50
+ eval <<EOS
51
+ module Object
52
+ class #{name.downcase.gsub(/(^|_)(.)/) { $2.upcase }} < OCI8::Object::Base
53
+ set_typename('#{name}')
54
+ end
55
+ end
56
+ EOS
57
+ klass = OCI8::Object::Base.get_class_by_typename(name)
58
+ else
59
+ eval <<EOS
60
+ module Object
61
+ module #{schema_name.downcase.gsub(/(^|_)(.)/) { $2.upcase }}
62
+ class #{name.downcase.gsub(/(^|_)(.)/) { $2.upcase }} < OCI8::Object::Base
63
+ set_typename('#{full_name}')
64
+ end
65
+ end
66
+ end
67
+ EOS
68
+ klass = OCI8::Object::Base.get_class_by_typename(full_name)
69
+ end
70
+ end
71
+ OCI8::TDO.new(self, metadata, klass)
72
+ end
73
+
74
+ # Returns the type descriptor object which correspond to the given type name.
75
+ #
76
+ # @param [String] typename
77
+ # @return [OCI8::TDO]
78
+ #
79
+ # @private
80
+ def get_tdo_by_typename(typename)
81
+ @name_to_tdo ||= {}
82
+ tdo = @name_to_tdo[typename]
83
+ return tdo if tdo
84
+
85
+ metadata = describe_any(typename)
86
+ if metadata.is_a? OCI8::Metadata::Synonym
87
+ metadata = describe_any(metadata.translated_name)
88
+ end
89
+ unless metadata.is_a? OCI8::Metadata::Type
90
+ raise "unknown typename #{typename}"
91
+ end
92
+ tdo = get_tdo_by_metadata(metadata)
93
+
94
+ @name_to_tdo[typename] = tdo
95
+ tdo
96
+ end
97
+
98
+ # A helper class to bind arguments.
99
+ #
100
+ # @private
101
+ class BindArgumentHelper
102
+ attr_reader :arg_str
103
+ def initialize(*args)
104
+ if args.length == 1 and args[0].is_a? Hash
105
+ @arg_str = args[0].keys.collect do |key| "#{key}=>:#{key}"; end.join(', ')
106
+ @bind_vars = args[0]
107
+ else
108
+ ary = []
109
+ @bind_vars = {}
110
+ args.each_with_index do |obj, idx|
111
+ key = ':' + (idx + 1).to_s
112
+ ary << key
113
+ @bind_vars[key] = obj
114
+ end
115
+ @arg_str = ary.join(', ')
116
+ end
117
+ end
118
+
119
+ def exec(con, csr)
120
+ @bind_vars.each do |key, val|
121
+ if val.is_a? OCI8::Object::Base
122
+ tdo = con.get_tdo_by_class(val.class)
123
+ csr.bind_param(key, nil, :named_type_internal, tdo)
124
+ csr[key].attributes = val
125
+ else
126
+ csr.bind_param(key, val ? val : '')
127
+ end
128
+ end
129
+ csr.exec
130
+ @bind_vars.each do |key, val|
131
+ if val.is_a? OCI8::Object::Base
132
+ val.instance_variable_set(:@attributes, csr[key].attributes)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ module Object
139
+ class Base
140
+ @@class_to_name = {}
141
+ @@name_to_class = {}
142
+ @@default_connection = nil
143
+
144
+ # @private
145
+ def self.inherited(klass)
146
+ name = klass.to_s.gsub(/^.*::/, '').gsub(/([a-z\d])([A-Z])/,'\1_\2').upcase
147
+ @@class_to_name[klass] = name
148
+ @@name_to_class[name] = klass
149
+ end
150
+
151
+ # @private
152
+ def self.get_class_by_typename(name)
153
+ @@name_to_class[name]
154
+ end
155
+
156
+ def self.typename
157
+ @@class_to_name[self]
158
+ end
159
+
160
+ def self.set_typename(name)
161
+ # delete an old name-to-class mapping.
162
+ @@name_to_class[@@class_to_name[self]] = nil
163
+ # set a new name-to-class mapping.
164
+ name = name.upcase
165
+ @@class_to_name[self] = name
166
+ @@name_to_class[name] = self
167
+ end
168
+
169
+ # @deprecated
170
+ def self.default_connection=(con)
171
+ @@default_connection = con
172
+ end
173
+
174
+ def initialize(*args)
175
+ @attributes = {}
176
+ if args[0].is_a? OCI8
177
+ @con = args.shift
178
+ else
179
+ @con = @@default_connection
180
+ end
181
+ return if args.empty?
182
+ raise "no connection is specified." if @con.nil?
183
+ # setup a TDO object.
184
+ tdo = @con.get_tdo_by_class(self.class)
185
+ # call constructor.
186
+ bind_arg_helper = BindArgumentHelper.new(*args)
187
+ sql = <<EOS
188
+ BEGIN
189
+ :self := #{tdo.typename}(#{bind_arg_helper.arg_str});
190
+ END;
191
+ EOS
192
+ csr = @con.parse_internal(sql)
193
+ begin
194
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
195
+ bind_arg_helper.exec(@con, csr)
196
+ @attributes = csr[:self].attributes
197
+ ensure
198
+ csr.close
199
+ end
200
+ end
201
+
202
+ # class method
203
+
204
+ # @private
205
+ def self.method_missing(method_id, *args)
206
+ if args[0].is_a? OCI8
207
+ con = args.shift
208
+ else
209
+ con = @@default_connection
210
+ end
211
+ tdo = con.get_tdo_by_class(self)
212
+ return_type = tdo.class_methods[method_id]
213
+ if return_type == :none
214
+ # procedure
215
+ bind_arg_helper = BindArgumentHelper.new(*args)
216
+ sql = <<EOS
217
+ BEGIN
218
+ #{tdo.typename}.#{method_id}(#{bind_arg_helper.arg_str});
219
+ END;
220
+ EOS
221
+ csr = con.parse_internal(sql)
222
+ begin
223
+ bind_arg_helper.exec(con, csr)
224
+ ensure
225
+ csr.close
226
+ end
227
+ return nil
228
+ elsif return_type
229
+ # function
230
+ return_type = tdo.class_methods[method_id]
231
+ bind_arg_helper = BindArgumentHelper.new(*args)
232
+ sql = <<EOS
233
+ BEGIN
234
+ :rv := #{tdo.typename}.#{method_id}(#{bind_arg_helper.arg_str});
235
+ END;
236
+ EOS
237
+ csr = con.parse_internal(sql)
238
+ begin
239
+ csr.bind_param(:rv, nil, return_type)
240
+ bind_arg_helper.exec(con, csr)
241
+ rv = csr[:rv]
242
+ ensure
243
+ csr.close
244
+ end
245
+ return rv
246
+ end
247
+ super # The method is not found.
248
+ end
249
+
250
+ # instance method
251
+
252
+ # @private
253
+ def method_missing(method_id, *args)
254
+ if @attributes.is_a? Array
255
+ return @attributes if method_id == :to_ary
256
+ super
257
+ end
258
+ # getter func
259
+ if @attributes.has_key?(method_id)
260
+ if args.length != 0
261
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0)"
262
+ end
263
+ return @attributes[method_id]
264
+ end
265
+ # setter func
266
+ method_name = method_id.to_s
267
+ if method_name[-1] == ?=
268
+ attr = method_name[0...-1].intern
269
+ if @attributes.has_key?(attr)
270
+ if args.length != 1
271
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1)"
272
+ end
273
+ return @attributes[attr] = args[0]
274
+ end
275
+ end
276
+
277
+ super if @con.nil?
278
+
279
+ tdo = @con.get_tdo_by_class(self.class)
280
+ return_type = tdo.instance_methods[method_id]
281
+ if return_type == :none
282
+ # procedure
283
+ bind_arg_helper = BindArgumentHelper.new(*args)
284
+ sql = <<EOS
285
+ DECLARE
286
+ val #{tdo.typename} := :self;
287
+ BEGIN
288
+ val.#{method_id}(#{bind_arg_helper.arg_str});
289
+ :self := val;
290
+ END;
291
+ EOS
292
+ csr = @con.parse_internal(sql)
293
+ begin
294
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
295
+ csr[:self].attributes = self
296
+ bind_arg_helper.exec(@con, csr)
297
+ @attributes = csr[:self].attributes
298
+ ensure
299
+ csr.close
300
+ end
301
+ return nil
302
+ elsif return_type
303
+ # function
304
+ bind_arg_helper = BindArgumentHelper.new(*args)
305
+ sql = <<EOS
306
+ DECLARE
307
+ val #{tdo.typename} := :self;
308
+ BEGIN
309
+ :rv := val.#{method_id}(#{bind_arg_helper.arg_str});
310
+ :self := val;
311
+ END;
312
+ EOS
313
+ csr = @con.parse_internal(sql)
314
+ begin
315
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
316
+ csr.bind_param(:rv, nil, return_type)
317
+ csr[:self].attributes = self
318
+ bind_arg_helper.exec(@con, csr)
319
+ @attributes = csr[:self].attributes
320
+ rv = csr[:rv]
321
+ ensure
322
+ csr.close
323
+ end
324
+ return rv
325
+ end
326
+ # The method is not found.
327
+ super
328
+ end # method_missing
329
+ end # OCI8::Object::Base
330
+ end # OCI8::Object
331
+
332
+ # @private
333
+ class TDO
334
+ # full-qualified object type name.
335
+ # e.g.
336
+ # MDSYS.SDO_GEOMETRY
337
+ attr_reader :typename
338
+
339
+ # a subclass of OCI8::Object::Base
340
+ attr_reader :ruby_class
341
+
342
+ attr_reader :val_size
343
+ attr_reader :ind_size
344
+ attr_reader :alignment
345
+
346
+ attr_reader :attributes
347
+ attr_reader :coll_attr
348
+ attr_reader :attr_getters
349
+ attr_reader :attr_setters
350
+
351
+ # mapping between class method's ids and their return types.
352
+ # :none means a procedure.
353
+ # CREATE OR REPLACE TYPE foo AS OBJECT (
354
+ # STATIC FUNCTION bar RETURN INTEGER,
355
+ # STATIC PROCEDURE baz,
356
+ # );
357
+ # => {:bar => Integer, :baz => :none}
358
+
359
+ attr_reader :class_methods
360
+ # mapping between instance method's ids and their return types.
361
+ # :none means a procedure.
362
+ # CREATE OR REPLACE TYPE foo AS OBJECT (
363
+ # MEMBER FUNCTION bar RETURN INTEGER,
364
+ # MEMBER PROCEDURE baz,
365
+ # );
366
+ # => {:bar => Integer, :baz => :none}
367
+ attr_reader :instance_methods
368
+
369
+ def is_collection?
370
+ @coll_attr ? true : false
371
+ end
372
+
373
+ def initialize(con, metadata, klass)
374
+ @ruby_class = klass
375
+ @typename = metadata.schema_name + '.' + metadata.name
376
+
377
+ setup(con, metadata)
378
+ con.instance_variable_get(:@id_to_tdo)[metadata.tdo_id] = self
379
+ con.instance_variable_get(:@name_to_tdo)[@typename] = self
380
+ con.instance_variable_get(:@name_to_tdo)[klass.typename] = self
381
+ if metadata.schema_name == con.username
382
+ con.instance_variable_get(:@name_to_tdo)[metadata.name] = self
383
+ end
384
+
385
+ case metadata.typecode
386
+ when :named_type
387
+ @is_final_type = metadata.is_final_type?
388
+ initialize_named_type(con, metadata)
389
+ when :named_collection
390
+ @is_final_type = true
391
+ initialize_named_collection(con, metadata)
392
+ end
393
+ end
394
+
395
+ def initialize_named_type(con, metadata)
396
+ @val_size = 0
397
+ @ind_size = 2
398
+ @alignment = 1
399
+ @attributes = metadata.type_attrs.collect do |type_attr|
400
+ attr = Attr.new(con, type_attr, @val_size, @ind_size)
401
+ @val_size, @ind_size = attr.next_offset
402
+ if @alignment < attr.alignment
403
+ @alignment = attr.alignment
404
+ end
405
+ attr
406
+ end
407
+ # fix alignment
408
+ @val_size = (@val_size + @alignment - 1) & ~(@alignment - 1)
409
+
410
+ # setup attr_getters and attr_setters
411
+ @attr_getters = {}
412
+ @attr_setters = {}
413
+ @attributes.each do |attr|
414
+ @attr_getters[attr.name] = attr
415
+ @attr_setters[(attr.name.to_s + '=').intern] = attr
416
+ end
417
+
418
+ # set class_methods and instance_methods
419
+ @class_methods = {}
420
+ @instance_methods = {}
421
+ metadata.type_methods.each_with_index do |type_method, i|
422
+ next if type_method.is_constructor? or type_method.is_destructor?
423
+
424
+ result_type = nil
425
+ if type_method.has_result?
426
+ # function
427
+ con.exec_internal("select result_type_owner, result_type_name from all_method_results where OWNER = :1 and TYPE_NAME = :2 and METHOD_NO = :3", metadata.schema_name, metadata.name, i + 1) do |r|
428
+ if r[0].nil?
429
+ result_type = @@result_type_to_bindtype[r[1]]
430
+ else
431
+ result_type = con.get_tdo_by_metadata(con.describe_type("#{r[0]}.#{r[1]}"))
432
+ end
433
+ end
434
+ else
435
+ # procedure
436
+ result_type = :none
437
+ end
438
+ if result_type
439
+ if type_method.is_selfish?
440
+ @instance_methods[type_method.name.downcase.intern] = result_type
441
+ else
442
+ @class_methods[type_method.name.downcase.intern] = result_type
443
+ end
444
+ else
445
+ warn "unsupported return type (#{metadata.schema_name}.#{metadata.name}.#{type_method.name})" if $VERBOSE
446
+ end
447
+ end
448
+ end
449
+ private :initialize_named_type
450
+
451
+ def initialize_named_collection(con, metadata)
452
+ @val_size = SIZE_OF_POINTER
453
+ @ind_size = 2
454
+ @alignment = ALIGNMENT_OF_POINTER
455
+ @coll_attr = Attr.new(con, metadata.collection_element, 0, 0)
456
+ end
457
+ private :initialize_named_collection
458
+
459
+ def inspect
460
+ "#<#{self.class}:#@typename>"
461
+ end
462
+
463
+ @@result_type_to_bindtype = {
464
+ 'FLOAT' => Float,
465
+ 'INTEGER' => Integer,
466
+ 'NUMBER' => OraNumber,
467
+ 'BINARY_FLOAT' => :binary_float,
468
+ 'BINARY_DOUBLE' => :binary_double,
469
+ 'TIMESTAMP' => :timestamp,
470
+ 'TIMESTAMP WITH TZ' => :timestamp_tz,
471
+ 'TIMESTAMP WITH LOCAL TZ' => :timestamp_ltz,
472
+ 'INTERVAL YEAR TO MONTH' => :interval_ym,
473
+ 'INTERVAL DAY TO SECOND' => :interval_ds,
474
+ }
475
+
476
+ # to use datetime_to_array and array_to_datetime
477
+ extend OCI8::BindType::Util
478
+
479
+ def self.check_metadata(con, metadata)
480
+ case metadata.typecode
481
+ when :char, :varchar, :varchar2
482
+ [ATTR_STRING, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
483
+ when :raw
484
+ [ATTR_RAW, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
485
+ when :number, :decimal
486
+ [ATTR_OCINUMBER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
487
+ when :integer, :smallint
488
+ [ATTR_INTEGER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
489
+ when :real, :double, :float
490
+ [ATTR_FLOAT, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
491
+ when :date
492
+ [ATTR_OCIDATE, nil, SIZE_OF_OCIDATE, 2, ALIGNMENT_OF_OCIDATE,
493
+ Proc.new do |val| datetime_to_array(val, :date) end, # set_proc
494
+ Proc.new do |val| array_to_time(val, :local) end, # get_proc
495
+ ]
496
+ when :timestamp
497
+ [ATTR_TIMESTAMP, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
498
+ Proc.new do |val| datetime_to_array(val, :timestamp) end, # set_proc
499
+ Proc.new do |val| array_to_time(val, :local) end, # get_proc
500
+ ]
501
+ when :timestamp_tz
502
+ [ATTR_TIMESTAMP_TZ, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
503
+ Proc.new do |val| datetime_to_array(val, :timestamp_tz) end, # set_proc
504
+ Proc.new do |val| array_to_time(val, nil) end, # get_proc
505
+ ]
506
+ when :binary_double
507
+ [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
508
+ when :binary_float
509
+ [ATTR_BINARY_FLOAT, nil, SIZE_OF_FLOAT, 2, ALIGNMENT_OF_FLOAT]
510
+ when :named_type
511
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
512
+ [ATTR_NAMED_TYPE, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
513
+ when :named_collection
514
+ #datatype, typeinfo, = OCI8::TDO.check_metadata(con, metadata.type_metadata.collection_element)
515
+ #[ATTR_NAMED_COLLECTION, [datatype, typeinfo], SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
516
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
517
+ [ATTR_NAMED_COLLECTION, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
518
+ when :clob
519
+ if metadata.charset_form != :nchar
520
+ [ATTR_CLOB, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
521
+ else
522
+ [ATTR_NCLOB, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
523
+ end
524
+ when :blob
525
+ [ATTR_BLOB, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
526
+ when :bfile
527
+ [ATTR_BFILE, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
528
+ else
529
+ raise "unsupported typecode #{metadata.typecode}"
530
+ end
531
+ end
532
+
533
+ class Attr
534
+ attr_reader :name
535
+ attr_reader :val_offset
536
+ attr_reader :ind_offset
537
+ attr_reader :alignment
538
+ attr_reader :datatype
539
+ attr_reader :typeinfo
540
+ attr_reader :set_proc
541
+ attr_reader :get_proc
542
+ def initialize(con, metadata, val_offset, ind_offset)
543
+ if metadata.respond_to? :name
544
+ @name = metadata.name.downcase.intern
545
+ end
546
+ @datatype, @typeinfo, @val_size, @ind_size, @alignment, @set_proc, @get_proc, = OCI8::TDO.check_metadata(con, metadata)
547
+ @val_offset = (val_offset + @alignment - 1) & ~(@alignment - 1)
548
+ @ind_offset = ind_offset
549
+ end
550
+ def next_offset
551
+ [@val_offset + @val_size, @ind_offset + @ind_size]
552
+ end
553
+ end
554
+ end
555
+
556
+ # @private
557
+ class NamedType
558
+ def to_value
559
+ return nil if self.null?
560
+ obj = tdo.ruby_class.new
561
+ obj.instance_variable_set(:@attributes, self.attributes)
562
+ obj
563
+ end
564
+
565
+ def attributes
566
+ attrs = {}
567
+ tdo.attributes.each do |attr|
568
+ attr_val = get_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset)
569
+ attr_val = attr.get_proc.call(attr_val) if attr.get_proc
570
+ attrs[attr.name] = attr_val
571
+ end
572
+ attrs
573
+ end
574
+
575
+ def attributes=(obj)
576
+ if obj.nil?
577
+ self.null = true
578
+ else
579
+ obj = obj.instance_variable_get(:@attributes) unless obj.is_a? Hash
580
+ tdo.attributes.each do |attr|
581
+ attr_val = obj[attr.name]
582
+ attr_val = attr.set_proc.call(attr_val) if attr.set_proc
583
+ set_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset, attr_val)
584
+ end
585
+ self.null = false
586
+ end
587
+ end
588
+ end
589
+
590
+ # @private
591
+ class NamedCollection
592
+ def to_value
593
+ attr = self.attributes
594
+ if attr
595
+ obj = tdo.ruby_class.new
596
+ obj.instance_variable_set(:@attributes, attr)
597
+ obj
598
+ end
599
+ end
600
+
601
+ def attributes
602
+ attr = tdo.coll_attr
603
+ get_coll_element(attr.datatype, attr.typeinfo)
604
+ end
605
+
606
+ def attributes=(obj)
607
+ attr = tdo.coll_attr
608
+ set_coll_element(attr.datatype, attr.typeinfo, obj.to_ary)
609
+ end
610
+ end
611
+ end
612
+
613
+ class OCI8
614
+ module BindType
615
+
616
+ class NamedType
617
+ def self.create(con, val, param, max_array_size)
618
+ case param
619
+ when Hash
620
+ self.new(con, val, param[:length], max_array_size)
621
+ else
622
+ self.new(con, val, param, max_array_size)
623
+ end
624
+ end
625
+ end
626
+
627
+ class Object < OCI8::BindType::NamedType
628
+ alias :get_orig get
629
+ def set(val)
630
+ get_orig.attributes = val
631
+ nil
632
+ end
633
+ def get()
634
+ (obj = super()) && obj.to_value
635
+ end
636
+ end
637
+ end
638
+ end
639
+
640
+ OCI8::BindType::Mapping[:named_type] = OCI8::BindType::Object
641
+ OCI8::BindType::Mapping[:named_type_internal] = OCI8::BindType::NamedType