ruby-oci8 2.2.10-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
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