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

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