ruby-oci8 2.0.4-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/ChangeLog +1912 -0
  2. data/Makefile +96 -0
  3. data/NEWS +223 -0
  4. data/README +86 -0
  5. data/VERSION +1 -0
  6. data/dist-files +77 -0
  7. data/doc/api.en.html +527 -0
  8. data/doc/api.en.rd +554 -0
  9. data/doc/api.ja.html +525 -0
  10. data/doc/api.ja.rd +557 -0
  11. data/doc/manual.css +35 -0
  12. data/lib/.document +1 -0
  13. data/lib/dbd/OCI8.rb +591 -0
  14. data/lib/oci8.rb +82 -0
  15. data/lib/oci8.rb.in +82 -0
  16. data/lib/oci8/.document +5 -0
  17. data/lib/oci8/bindtype.rb +319 -0
  18. data/lib/oci8/compat.rb +113 -0
  19. data/lib/oci8/datetime.rb +619 -0
  20. data/lib/oci8/encoding-init.rb +40 -0
  21. data/lib/oci8/encoding.yml +537 -0
  22. data/lib/oci8/metadata.rb +2077 -0
  23. data/lib/oci8/object.rb +562 -0
  24. data/lib/oci8/oci8.rb +571 -0
  25. data/lib/oci8/oracle_version.rb +144 -0
  26. data/lib/oci8lib_18.so +0 -0
  27. data/lib/oci8lib_191.so +0 -0
  28. data/metaconfig +142 -0
  29. data/pre-distclean.rb +7 -0
  30. data/ruby-oci8.gemspec +63 -0
  31. data/setup.rb +1331 -0
  32. data/test/README +4 -0
  33. data/test/config.rb +109 -0
  34. data/test/test_all.rb +50 -0
  35. data/test/test_appinfo.rb +63 -0
  36. data/test/test_array_dml.rb +333 -0
  37. data/test/test_bind_raw.rb +46 -0
  38. data/test/test_bind_time.rb +178 -0
  39. data/test/test_break.rb +83 -0
  40. data/test/test_clob.rb +79 -0
  41. data/test/test_connstr.rb +81 -0
  42. data/test/test_datetime.rb +622 -0
  43. data/test/test_dbi.rb +366 -0
  44. data/test/test_dbi_clob.rb +53 -0
  45. data/test/test_encoding.rb +100 -0
  46. data/test/test_metadata.rb +257 -0
  47. data/test/test_oci8.rb +434 -0
  48. data/test/test_oracle_version.rb +70 -0
  49. data/test/test_oradate.rb +256 -0
  50. data/test/test_oranumber.rb +655 -0
  51. data/test/test_rowid.rb +33 -0
  52. metadata +108 -0
@@ -0,0 +1,562 @@
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 ? 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
+ # for datetime_to_array and ocidate_to_datetime
429
+ extend OCI8::BindType::Util
430
+
431
+ def self.check_metadata(con, metadata)
432
+ case metadata.typecode
433
+ when :char, :varchar, :varchar2
434
+ [ATTR_STRING, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
435
+ when :raw
436
+ [ATTR_RAW, nil, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
437
+ when :number, :decimal
438
+ [ATTR_OCINUMBER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
439
+ when :integer, :smallint
440
+ [ATTR_INTEGER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
441
+ when :real, :double, :float
442
+ [ATTR_FLOAT, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
443
+ when :date
444
+ [ATTR_OCIDATE, nil, SIZE_OF_OCIDATE, 2, ALIGNMENT_OF_OCIDATE,
445
+ Proc.new do |val| datetime_to_array(val, false) end, # set_proc
446
+ Proc.new do |val| ocidate_to_datetime(val) end, # get_proc
447
+ ]
448
+ when :binary_double
449
+ [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
450
+ when :binary_float
451
+ [ATTR_BINARY_FLOAT, nil, SIZE_OF_FLOAT, 2, ALIGNMENT_OF_FLOAT]
452
+ when :named_type
453
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
454
+ [ATTR_NAMED_TYPE, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
455
+ when :named_collection
456
+ #datatype, typeinfo, = OCI8::TDO.check_metadata(con, metadata.type_metadata.collection_element)
457
+ #[ATTR_NAMED_COLLECTION, [datatype, typeinfo], SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER]
458
+ tdo = con.get_tdo_by_metadata(metadata.type_metadata)
459
+ [ATTR_NAMED_COLLECTION, tdo, tdo.val_size, tdo.ind_size, tdo.alignment]
460
+ else
461
+ raise "unsupported typecode #{metadata.typecode}"
462
+ end
463
+ end
464
+
465
+ class Attr
466
+ attr_reader :name
467
+ attr_reader :val_offset
468
+ attr_reader :ind_offset
469
+ attr_reader :alignment
470
+ attr_reader :datatype
471
+ attr_reader :typeinfo
472
+ attr_reader :set_proc
473
+ attr_reader :get_proc
474
+ def initialize(con, metadata, val_offset, ind_offset)
475
+ if metadata.respond_to? :name
476
+ @name = metadata.name.downcase.intern
477
+ end
478
+ @datatype, @typeinfo, @val_size, @ind_size, @alignment, @set_proc, @get_proc, = OCI8::TDO.check_metadata(con, metadata)
479
+ @val_offset = (val_offset + @alignment - 1) & ~(@alignment - 1)
480
+ @ind_offset = ind_offset
481
+ end
482
+ def next_offset
483
+ [@val_offset + @val_size, @ind_offset + @ind_size]
484
+ end
485
+ end
486
+ end
487
+
488
+ class NamedType
489
+ def to_value
490
+ obj = tdo.ruby_class.new
491
+ obj.instance_variable_set(:@attributes, self.attributes)
492
+ obj
493
+ end
494
+
495
+ def attributes
496
+ attrs = {}
497
+ tdo.attributes.each do |attr|
498
+ attr_val = get_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset)
499
+ attr_val = attr.get_proc.call(attr_val) if attr.get_proc
500
+ attrs[attr.name] = attr_val
501
+ end
502
+ attrs
503
+ end
504
+
505
+ def attributes=(obj)
506
+ obj = obj.instance_variable_get(:@attributes) unless obj.is_a? Hash
507
+ tdo.attributes.each do |attr|
508
+ attr_val = obj[attr.name]
509
+ attr_val = attr.set_proc.call(attr_val) if attr.set_proc
510
+ set_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset, attr_val)
511
+ end
512
+ end
513
+ end
514
+
515
+ class NamedCollection
516
+ def to_value
517
+ obj = tdo.ruby_class.new
518
+ obj.instance_variable_set(:@attributes, self.attributes)
519
+ obj
520
+ end
521
+
522
+ def attributes
523
+ attr = tdo.coll_attr
524
+ get_coll_element(attr.datatype, attr.typeinfo)
525
+ end
526
+
527
+ def attributes=(obj)
528
+ attr = tdo.coll_attr
529
+ set_coll_element(attr.datatype, attr.typeinfo, obj.to_ary)
530
+ end
531
+ end
532
+ end
533
+
534
+ class OCI8
535
+ module BindType
536
+
537
+ class NamedType
538
+ def self.create(con, val, param, max_array_size)
539
+ case param
540
+ when Hash
541
+ self.new(con, val, param[:length], max_array_size)
542
+ else
543
+ self.new(con, val, param, max_array_size)
544
+ end
545
+ end
546
+ end
547
+
548
+ class Object < OCI8::BindType::NamedType
549
+ alias :get_orig get
550
+ def set(val)
551
+ get_orig.attributes = val
552
+ nil
553
+ end
554
+ def get()
555
+ (obj = super()) && obj.to_value
556
+ end
557
+ end
558
+ end
559
+ end
560
+
561
+ OCI8::BindType::Mapping[:named_type] = OCI8::BindType::Object
562
+ OCI8::BindType::Mapping[:named_type_internal] = OCI8::BindType::NamedType