ruby-oci8 1.0.7-x86-mswin32-60 → 2.0.1-x86-mswin32-60

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 (58) hide show
  1. data/ChangeLog +1289 -383
  2. data/Makefile +48 -12
  3. data/NEWS +5 -419
  4. data/README +56 -385
  5. data/VERSION +1 -1
  6. data/dist-files +27 -27
  7. data/lib/.document +2 -0
  8. data/lib/dbd/OCI8.rb +2 -17
  9. data/lib/oci8.rb +48 -1622
  10. data/lib/oci8.rb.in +48 -1622
  11. data/lib/oci8/.document +5 -0
  12. data/lib/oci8/compat.rb +108 -0
  13. data/lib/oci8/datetime.rb +491 -0
  14. data/lib/oci8/encoding-init.rb +40 -0
  15. data/lib/oci8/encoding.yml +537 -0
  16. data/lib/oci8/metadata.rb +2077 -0
  17. data/lib/oci8/object.rb +548 -0
  18. data/lib/oci8/oci8.rb +798 -0
  19. data/lib/oci8/oracle_version.rb +144 -0
  20. data/lib/oci8lib_18.so +0 -0
  21. data/lib/oci8lib_191.so +0 -0
  22. data/metaconfig +3 -3
  23. data/ruby-oci8.gemspec +24 -15
  24. data/setup.rb +4 -4
  25. data/test/config.rb +64 -84
  26. data/test/test_all.rb +14 -21
  27. data/test/test_array_dml.rb +333 -0
  28. data/test/test_bind_raw.rb +18 -25
  29. data/test/test_bind_time.rb +78 -91
  30. data/test/test_break.rb +37 -35
  31. data/test/test_clob.rb +33 -89
  32. data/test/test_connstr.rb +5 -4
  33. data/test/test_datetime.rb +469 -0
  34. data/test/test_dbi.rb +99 -60
  35. data/test/test_dbi_clob.rb +3 -8
  36. data/test/test_metadata.rb +65 -51
  37. data/test/test_oci8.rb +151 -55
  38. data/test/test_oracle_version.rb +70 -0
  39. data/test/test_oradate.rb +76 -83
  40. data/test/test_oranumber.rb +405 -71
  41. data/test/test_rowid.rb +6 -11
  42. metadata +21 -25
  43. data/ext/oci8/oci8lib.so +0 -0
  44. data/ruby-oci8.spec +0 -62
  45. data/support/README +0 -4
  46. data/support/runit/assert.rb +0 -281
  47. data/support/runit/cui/testrunner.rb +0 -101
  48. data/support/runit/error.rb +0 -4
  49. data/support/runit/method_mappable.rb +0 -20
  50. data/support/runit/robserver.rb +0 -25
  51. data/support/runit/setuppable.rb +0 -15
  52. data/support/runit/teardownable.rb +0 -16
  53. data/support/runit/testcase.rb +0 -113
  54. data/support/runit/testfailure.rb +0 -25
  55. data/support/runit/testresult.rb +0 -121
  56. data/support/runit/testsuite.rb +0 -43
  57. data/support/runit/version.rb +0 -3
  58. data/test/test_describe.rb +0 -137
@@ -0,0 +1,548 @@
1
+ #
2
+ # OCI8::NamedType
3
+ #
4
+ require 'oci8/metadata.rb'
5
+
6
+ class OCI8
7
+
8
+ def get_tdo_by_class(klass)
9
+ @id_to_tdo ||= {}
10
+ @name_to_tdo ||= {}
11
+ tdo = @name_to_tdo[klass.typename]
12
+ return tdo if tdo
13
+
14
+ metadata = describe_any(klass.typename)
15
+ if metadata.is_a? OCI8::Metadata::Synonym
16
+ metadata = describe_any(metadata.translated_name)
17
+ end
18
+ unless metadata.is_a? OCI8::Metadata::Type
19
+ raise "unknown typename #{klass.typename}"
20
+ end
21
+ OCI8::TDO.new(self, metadata, klass)
22
+ end
23
+
24
+ def get_tdo_by_metadata(metadata)
25
+ @id_to_tdo ||= {}
26
+ @name_to_tdo ||= {}
27
+ tdo = @id_to_tdo[metadata.tdo_id]
28
+ return tdo if tdo
29
+
30
+ schema_name = metadata.schema_name
31
+ name = metadata.name
32
+ full_name = schema_name + '.' + name
33
+
34
+ klass = OCI8::Object::Base.get_class_by_typename(full_name)
35
+ klass = OCI8::Object::Base.get_class_by_typename(name) if klass.nil?
36
+ if klass.nil?
37
+ if schema_name == username
38
+ eval <<EOS
39
+ module Object
40
+ class #{name.downcase.gsub(/(^|_)(.)/) { $2.upcase }} < OCI8::Object::Base
41
+ set_typename('#{name}')
42
+ end
43
+ end
44
+ EOS
45
+ klass = OCI8::Object::Base.get_class_by_typename(name)
46
+ else
47
+ eval <<EOS
48
+ module Object
49
+ module #{schema_name.downcase.gsub(/(^|_)(.)/) { $2.upcase }}
50
+ class #{name.downcase.gsub(/(^|_)(.)/) { $2.upcase }} < OCI8::Object::Base
51
+ set_typename('#{full_name}')
52
+ end
53
+ end
54
+ end
55
+ EOS
56
+ klass = OCI8::Object::Base.get_class_by_typename(full_name)
57
+ end
58
+ end
59
+ OCI8::TDO.new(self, metadata, klass)
60
+ end
61
+
62
+ class BindArgumentHelper # :nodoc:
63
+ attr_reader :arg_str
64
+ def initialize(*args)
65
+ if args.length == 1 and args[0].is_a? Hash
66
+ @arg_str = args[0].keys.collect do |key| "#{key}=>:#{key}"; end.join(', ')
67
+ @bind_vars = args[0]
68
+ else
69
+ ary = []
70
+ @bind_vars = {}
71
+ args.each_with_index do |obj, idx|
72
+ key = ':' + (idx + 1).to_s
73
+ ary << key
74
+ @bind_vars[key] = obj
75
+ end
76
+ @arg_str = ary.join(', ')
77
+ end
78
+ end
79
+
80
+ def exec(con, csr)
81
+ @bind_vars.each do |key, val|
82
+ if val.is_a? OCI8::Object::Base
83
+ tdo = con.get_tdo_by_class(val.class)
84
+ csr.bind_param(key, nil, :named_type_internal, tdo)
85
+ csr[key].attributes = val
86
+ else
87
+ csr.bind_param(key, val)
88
+ end
89
+ end
90
+ csr.exec
91
+ @bind_vars.each do |key, val|
92
+ if val.is_a? OCI8::Object::Base
93
+ val.instance_variable_set(:@attributes, csr[key].attributes)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ module Object
100
+ class Base
101
+ @@class_to_name = {}
102
+ @@name_to_class = {}
103
+ @@default_connection = nil
104
+
105
+ def self.inherited(klass)
106
+ name = klass.to_s.gsub(/^.*::/, '').gsub(/([a-z\d])([A-Z])/,'\1_\2').upcase
107
+ @@class_to_name[klass] = name
108
+ @@name_to_class[name] = klass
109
+ end
110
+
111
+ def self.get_class_by_typename(name)
112
+ @@name_to_class[name]
113
+ end
114
+
115
+ def self.typename
116
+ @@class_to_name[self]
117
+ end
118
+
119
+ def self.set_typename(name)
120
+ # delete an old name-to-class mapping.
121
+ @@name_to_class[@@class_to_name[self]] = nil
122
+ # set a new name-to-class mapping.
123
+ name = name.upcase
124
+ @@class_to_name[self] = name
125
+ @@name_to_class[name] = self
126
+ end
127
+
128
+ def self.default_connection=(con)
129
+ @@default_connection = con
130
+ end
131
+
132
+ def initialize(*args)
133
+ @attributes = {}
134
+ if args[0].is_a? OCI8
135
+ @con = args.shift
136
+ else
137
+ @con = @@default_connection
138
+ end
139
+ return if args.empty?
140
+ raise "no connection is specified." if @con.nil?
141
+ # setup a TDO object.
142
+ tdo = @con.get_tdo_by_class(self.class)
143
+ # call constructor.
144
+ bind_arg_helper = BindArgumentHelper.new(*args)
145
+ sql = <<EOS
146
+ BEGIN
147
+ :self := #{tdo.typename}(#{bind_arg_helper.arg_str});
148
+ END;
149
+ EOS
150
+ csr = @con.parse(sql)
151
+ begin
152
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
153
+ bind_arg_helper.exec(@con, csr)
154
+ @attributes = csr[:self].attributes
155
+ ensure
156
+ csr.close
157
+ end
158
+ end
159
+
160
+ # class method
161
+ def self.method_missing(method_id, *args)
162
+ if args[0].is_a? OCI8
163
+ con = args.shift
164
+ else
165
+ con = @@default_connection
166
+ end
167
+ tdo = con.get_tdo_by_class(self)
168
+ return_type = tdo.class_methods[method_id]
169
+ if return_type == :none
170
+ # procedure
171
+ bind_arg_helper = BindArgumentHelper.new(*args)
172
+ sql = <<EOS
173
+ BEGIN
174
+ #{tdo.typename}.#{method_id}(#{bind_arg_helper.arg_str});
175
+ END;
176
+ EOS
177
+ csr = con.parse(sql)
178
+ begin
179
+ bind_arg_helper.exec(con, csr)
180
+ ensure
181
+ csr.close
182
+ end
183
+ return nil
184
+ elsif return_type
185
+ # function
186
+ return_type = tdo.class_methods[method_id]
187
+ bind_arg_helper = BindArgumentHelper.new(*args)
188
+ sql = <<EOS
189
+ BEGIN
190
+ :rv := #{tdo.typename}.#{method_id}(#{bind_arg_helper.arg_str});
191
+ END;
192
+ EOS
193
+ csr = con.parse(sql)
194
+ begin
195
+ csr.bind_param(:rv, nil, return_type)
196
+ bind_arg_helper.exec(con, csr)
197
+ rv = csr[:rv]
198
+ ensure
199
+ csr.close
200
+ end
201
+ return rv
202
+ end
203
+ super # The method is not found.
204
+ end
205
+
206
+ # instance method
207
+ def method_missing(method_id, *args)
208
+ if @attributes.is_a? Array
209
+ return @attributes if method_id == :to_ary
210
+ super
211
+ end
212
+ # getter func
213
+ if @attributes.has_key?(method_id)
214
+ if args.length != 0
215
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0)"
216
+ end
217
+ return @attributes[method_id]
218
+ end
219
+ # setter func
220
+ method_name = method_id.to_s
221
+ if method_name[-1] == ?=
222
+ attr = method_name[0...-1].intern
223
+ if @attributes.has_key?(attr)
224
+ if args.length != 1
225
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1)"
226
+ end
227
+ return @attributes[attr] = args[0]
228
+ end
229
+ end
230
+
231
+ super if @con.nil?
232
+
233
+ tdo = @con.get_tdo_by_class(self.class)
234
+ return_type = tdo.instance_methods[method_id]
235
+ if return_type == :none
236
+ # procedure
237
+ bind_arg_helper = BindArgumentHelper.new(*args)
238
+ sql = <<EOS
239
+ DECLARE
240
+ val #{tdo.typename} := :self;
241
+ BEGIN
242
+ val.#{method_id}(#{bind_arg_helper.arg_str});
243
+ :self := val;
244
+ END;
245
+ EOS
246
+ csr = @con.parse(sql)
247
+ begin
248
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
249
+ csr[:self].attributes = self
250
+ bind_arg_helper.exec(@con, csr)
251
+ @attributes = csr[:self].attributes
252
+ ensure
253
+ csr.close
254
+ end
255
+ return nil
256
+ elsif return_type
257
+ # function
258
+ bind_arg_helper = BindArgumentHelper.new(*args)
259
+ sql = <<EOS
260
+ DECLARE
261
+ val #{tdo.typename} := :self;
262
+ BEGIN
263
+ :rv := val.#{method_id}(#{bind_arg_helper.arg_str});
264
+ :self := val;
265
+ END;
266
+ EOS
267
+ csr = @con.parse(sql)
268
+ begin
269
+ csr.bind_param(:self, nil, :named_type_internal, tdo)
270
+ csr.bind_param(:rv, nil, return_type)
271
+ csr[:self].attributes = self
272
+ bind_arg_helper.exec(@con, csr)
273
+ @attributes = csr[:self].attributes
274
+ rv = csr[:rv]
275
+ ensure
276
+ csr.close
277
+ end
278
+ return rv
279
+ end
280
+ # The method is not found.
281
+ super
282
+ end # method_missing
283
+ end # OCI8::Object::Base
284
+ end # OCI8::Object
285
+
286
+ class TDO
287
+ # full-qualified object type name.
288
+ # e.g.
289
+ # MDSYS.SDO_GEOMETRY
290
+ attr_reader :typename
291
+
292
+ # named_type
293
+ attr_reader :ruby_class
294
+
295
+ attr_reader :val_size
296
+ attr_reader :ind_size
297
+ attr_reader :alignment
298
+
299
+ attr_reader :attributes
300
+ attr_reader :coll_attr
301
+ attr_reader :attr_getters
302
+ attr_reader :attr_setters
303
+
304
+ # mapping between class method's ids and their return types.
305
+ # :none means a procedure.
306
+ # CREATE OR REPLACE TYPE foo AS OBJECT (
307
+ # STATIC FUNCTION bar RETURN INTEGER,
308
+ # STATIC PROCEDURE baz,
309
+ # );
310
+ # => {:bar => Integer, :baz => :none}
311
+
312
+ attr_reader :class_methods
313
+ # mapping between instance method's ids and their return types.
314
+ # :none means a procedure.
315
+ # CREATE OR REPLACE TYPE foo AS OBJECT (
316
+ # MEMBER FUNCTION bar RETURN INTEGER,
317
+ # MEMBER PROCEDURE baz,
318
+ # );
319
+ # => {:bar => Integer, :baz => :none}
320
+ attr_reader :instance_methods
321
+
322
+ def is_collection?
323
+ @coll_attr ? true : false
324
+ end
325
+
326
+ def initialize(con, metadata, klass)
327
+ @ruby_class = klass
328
+ @typename = metadata.schema_name + '.' + metadata.name
329
+
330
+ setup(con, metadata)
331
+ con.instance_variable_get(:@id_to_tdo)[metadata.tdo_id] = self
332
+ con.instance_variable_get(:@name_to_tdo)[@typename] = self
333
+ con.instance_variable_get(:@name_to_tdo)[klass.typename] = self
334
+ if metadata.schema_name == con.username
335
+ con.instance_variable_get(:@name_to_tdo)[metadata.name] = self
336
+ end
337
+
338
+ case metadata.typecode
339
+ when :named_type
340
+ initialize_named_type(con, metadata)
341
+ when :named_collection
342
+ initialize_named_collection(con, metadata)
343
+ end
344
+ end
345
+
346
+ def initialize_named_type(con, metadata)
347
+ @val_size = 0
348
+ @ind_size = 2
349
+ @alignment = 1
350
+ @attributes = metadata.type_attrs.collect do |type_attr|
351
+ attr = Attr.new(con, type_attr, @val_size, @ind_size)
352
+ @val_size, @ind_size = attr.next_offset
353
+ if @alignment < attr.alignment
354
+ @alignment = attr.alignment
355
+ end
356
+ attr
357
+ end
358
+ # fix alignment
359
+ @val_size = (@val_size + @alignment - 1) & ~(@alignment - 1)
360
+
361
+ # setup attr_getters and attr_setters
362
+ @attr_getters = {}
363
+ @attr_setters = {}
364
+ @attributes.each do |attr|
365
+ @attr_getters[attr.name] = attr
366
+ @attr_setters[(attr.name.to_s + '=').intern] = attr
367
+ end
368
+
369
+ # set class_methods and instance_methods
370
+ @class_methods = {}
371
+ @instance_methods = {}
372
+ metadata.type_methods.each_with_index do |type_method, i|
373
+ next if type_method.is_constructor? or type_method.is_destructor?
374
+ args = type_method.arguments
375
+
376
+ result_type = nil
377
+ if type_method.has_result?
378
+ # function
379
+ con.exec("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|
380
+ if r[0].nil?
381
+ result_type = @@result_type_to_bindtype[r[1]]
382
+ else
383
+ result_type = con.get_tdo_by_metadata(con.describe_type("#{r[0]}.#{r[1]}"))
384
+ end
385
+ end
386
+ else
387
+ # procedure
388
+ result_type = :none
389
+ end
390
+ if result_type
391
+ if type_method.is_selfish?
392
+ @instance_methods[type_method.name.downcase.intern] = result_type
393
+ else
394
+ @class_methods[type_method.name.downcase.intern] = result_type
395
+ end
396
+ else
397
+ warn "unsupported return type (#{schema_name}.#{name}.#{type_method.name})" if $VERBOSE
398
+ end
399
+ end
400
+ end
401
+ private :initialize_named_type
402
+
403
+ def initialize_named_collection(con, metadata)
404
+ @val_size = SIZE_OF_POINTER
405
+ @ind_size = 2
406
+ @alignment = ALIGNMENT_OF_POINTER
407
+ @coll_attr = Attr.new(con, metadata.collection_element, 0, 0)
408
+ end
409
+ private :initialize_named_collection
410
+
411
+ def inspect
412
+ "#<#{self.class}:#@typename>"
413
+ end
414
+
415
+ @@result_type_to_bindtype = {
416
+ 'FLOAT' => Float,
417
+ 'INTEGER' => Integer,
418
+ 'NUMBER' => OraNumber,
419
+ 'BINARY_FLOAT' => :binary_float,
420
+ 'BINARY_DOUBLE' => :binary_double,
421
+ 'TIMESTAMP' => :timestamp,
422
+ 'TIMESTAMP WITH TZ' => :timestamp_tz,
423
+ 'TIMESTAMP WITH LOCAL TZ' => :timestamp_ltz,
424
+ 'INTERVAL YEAR TO MONTH' => :interval_ym,
425
+ 'INTERVAL DAY TO SECOND' => :interval_ds,
426
+ }
427
+
428
+ def self.check_metadata(con, metadata)
429
+ case metadata.typecode
430
+ when :char, :varchar, :varchar2
431
+ [ATTR_STRING, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
432
+ when :raw
433
+ [ATTR_RAW, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
434
+ when :number, :decimal
435
+ [ATTR_OCINUMBER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
436
+ when :integer, :smallint
437
+ [ATTR_INTEGER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
438
+ when :real, :double, :float
439
+ [ATTR_FLOAT, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
440
+ when :binary_double
441
+ [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
442
+ when :binary_float
443
+ [ATTR_BINARY_FLOAT, nil, SIZE_OF_FLOAT, 2, ALIGNMENT_OF_FLOAT]
444
+ when :named_type
445
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
446
+ [ATTR_NAMED_TYPE, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
447
+ when :named_collection
448
+ #datatype, typeinfo, = OCI8::TDO.check_metadata(con, metadata.type_metadata.collection_element)
449
+ #[ATTR_NAMED_COLLECTION, [datatype, typeinfo], SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
450
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
451
+ [ATTR_NAMED_COLLECTION, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
452
+ else
453
+ raise "unsupported typecode #{metadata.typecode}"
454
+ end
455
+ end
456
+
457
+ class Attr
458
+ attr_reader :name
459
+ attr_reader :val_offset
460
+ attr_reader :ind_offset
461
+ attr_reader :alignment
462
+ attr_reader :datatype
463
+ attr_reader :typeinfo
464
+ def initialize(con, metadata, val_offset, ind_offset)
465
+ if metadata.respond_to? :name
466
+ @name = metadata.name.downcase.intern
467
+ end
468
+ @datatype, @typeinfo, @val_size, @ind_size, @alignment, = OCI8::TDO.check_metadata(con, metadata)
469
+ @val_offset = (val_offset + @alignment - 1) & ~(@alignment - 1)
470
+ @ind_offset = ind_offset
471
+ end
472
+ def next_offset
473
+ [@val_offset + @val_size, @ind_offset + @ind_size]
474
+ end
475
+ end
476
+ end
477
+
478
+ class NamedType
479
+ def to_value
480
+ obj = tdo.ruby_class.new
481
+ obj.instance_variable_set(:@attributes, self.attributes)
482
+ obj
483
+ end
484
+
485
+ def attributes
486
+ attrs = {}
487
+ tdo.attributes.each do |attr|
488
+ attrs[attr.name] = get_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset)
489
+ end
490
+ attrs
491
+ end
492
+
493
+ def attributes=(obj)
494
+ obj = obj.instance_variable_get(:@attributes) unless obj.is_a? Hash
495
+ tdo.attributes.each do |attr|
496
+ set_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset, obj[attr.name])
497
+ end
498
+ end
499
+ end
500
+
501
+ class NamedCollection
502
+ def to_value
503
+ obj = tdo.ruby_class.new
504
+ obj.instance_variable_set(:@attributes, self.attributes)
505
+ obj
506
+ end
507
+
508
+ def attributes
509
+ attr = tdo.coll_attr
510
+ get_coll_element(attr.datatype, attr.typeinfo)
511
+ end
512
+
513
+ def attributes=(obj)
514
+ attr = tdo.coll_attr
515
+ set_coll_element(attr.datatype, attr.typeinfo, obj.to_ary)
516
+ end
517
+ end
518
+ end
519
+
520
+ class OCI8
521
+ module BindType
522
+
523
+ class NamedType
524
+ def self.create(con, val, param, max_array_size)
525
+ case param
526
+ when Hash
527
+ self.new(con, val, param[:length], max_array_size)
528
+ else
529
+ self.new(con, val, param, max_array_size)
530
+ end
531
+ end
532
+ end
533
+
534
+ class Object < OCI8::BindType::NamedType
535
+ alias :get_orig get
536
+ def set(val)
537
+ get_orig.attributes = val
538
+ nil
539
+ end
540
+ def get()
541
+ (obj = super()) && obj.to_value
542
+ end
543
+ end
544
+ end
545
+ end
546
+
547
+ OCI8::BindType::Mapping[:named_type] = OCI8::BindType::Object
548
+ OCI8::BindType::Mapping[:named_type_internal] = OCI8::BindType::NamedType