ruby-oci8 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -84,7 +84,7 @@ EOS
84
84
  csr.bind_param(key, nil, :named_type_internal, tdo)
85
85
  csr[key].attributes = val
86
86
  else
87
- csr.bind_param(key, val)
87
+ csr.bind_param(key, val ? val : '')
88
88
  end
89
89
  end
90
90
  csr.exec
@@ -425,6 +425,9 @@ EOS
425
425
  'INTERVAL DAY TO SECOND' => :interval_ds,
426
426
  }
427
427
 
428
+ # for datetime_to_array and ocidate_to_datetime
429
+ extend OCI8::BindType::Util
430
+
428
431
  def self.check_metadata(con, metadata)
429
432
  case metadata.typecode
430
433
  when :char, :varchar, :varchar2
@@ -437,6 +440,11 @@ EOS
437
440
  [ATTR_INTEGER, nil, SIZE_OF_OCINUMBER, 2, ALIGNMENT_OF_OCINUMBER]
438
441
  when :real, :double, :float
439
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
+ ]
440
448
  when :binary_double
441
449
  [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
442
450
  when :binary_float
@@ -461,11 +469,13 @@ EOS
461
469
  attr_reader :alignment
462
470
  attr_reader :datatype
463
471
  attr_reader :typeinfo
472
+ attr_reader :set_proc
473
+ attr_reader :get_proc
464
474
  def initialize(con, metadata, val_offset, ind_offset)
465
475
  if metadata.respond_to? :name
466
476
  @name = metadata.name.downcase.intern
467
477
  end
468
- @datatype, @typeinfo, @val_size, @ind_size, @alignment, = OCI8::TDO.check_metadata(con, metadata)
478
+ @datatype, @typeinfo, @val_size, @ind_size, @alignment, @set_proc, @get_proc, = OCI8::TDO.check_metadata(con, metadata)
469
479
  @val_offset = (val_offset + @alignment - 1) & ~(@alignment - 1)
470
480
  @ind_offset = ind_offset
471
481
  end
@@ -485,7 +495,9 @@ EOS
485
495
  def attributes
486
496
  attrs = {}
487
497
  tdo.attributes.each do |attr|
488
- attrs[attr.name] = get_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset)
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
489
501
  end
490
502
  attrs
491
503
  end
@@ -493,7 +505,9 @@ EOS
493
505
  def attributes=(obj)
494
506
  obj = obj.instance_variable_get(:@attributes) unless obj.is_a? Hash
495
507
  tdo.attributes.each do |attr|
496
- set_attribute(attr.datatype, attr.typeinfo, attr.val_offset, attr.ind_offset, obj[attr.name])
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)
497
511
  end
498
512
  end
499
513
  end
@@ -1,16 +1,28 @@
1
- #--
2
- # oci8.rb -- OCI8 and OCI8::Cursor
1
+ # oci8.rb -- implements OCI8 and OCI8::Cursor
3
2
  #
4
3
  # Copyright (C) 2002-2009 KUBO Takehiro <kubo@jiubao.org>
5
4
  #
6
5
  # Original Copyright is:
7
6
  # Oracle module for Ruby
8
7
  # 1998-2000 by yoshidam
9
- #++
8
+ #
10
9
 
11
10
  require 'date'
12
11
 
13
- # The database connection class.
12
+ # A connection to a Oracle database server.
13
+ #
14
+ # example:
15
+ # # output the emp table's content as CSV format.
16
+ # conn = OCI8.new(username, password)
17
+ # conn.exec('select * from emp') do |row|
18
+ # puts row.join(',')
19
+ # end
20
+ #
21
+ # # execute PL/SQL block with bind variables.
22
+ # conn = OCI8.new(username, password)
23
+ # conn.exec('BEGIN procedure_name(:1, :2); END;',
24
+ # value_for_the_first_parameter,
25
+ # value_for_the_second_parameter)
14
26
  class OCI8
15
27
  # Executes the sql statement. The type of return value depends on
16
28
  # the type of sql statement: select; insert, update and delete;
@@ -331,6 +343,10 @@ class OCI8
331
343
  end
332
344
 
333
345
  bindclass = OCI8::BindType::Mapping[type]
346
+ if bindclass.nil? and type.is_a? Class
347
+ bindclass = OCI8::BindType::Mapping[type.to_s]
348
+ OCI8::BindType::Mapping[type] = bindclass if bindclass
349
+ end
334
350
  raise "unsupported dataType: #{type}" if bindclass.nil?
335
351
  bindobj = bindclass.create(@con, var_array, param, @max_array_size)
336
352
  __bind(key, bindobj)
@@ -440,6 +456,10 @@ class OCI8
440
456
  end
441
457
 
442
458
  bindclass = OCI8::BindType::Mapping[key]
459
+ if bindclass.nil? and key.is_a? Class
460
+ bindclass = OCI8::BindType::Mapping[key.to_s]
461
+ OCI8::BindType::Mapping[key] = bindclass if bindclass
462
+ end
443
463
  raise "unsupported datatype: #{key}" if bindclass.nil?
444
464
  bindclass.create(@con, val, param, max_array_size)
445
465
  end
@@ -23,6 +23,40 @@ class TestAppInfo < Test::Unit::TestCase
23
23
  assert_nil(@conn.select_one("SELECT SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER') FROM DUAL")[0]);
24
24
  end
25
25
 
26
+ def test_set_module
27
+ # FIXME: check again after upgrading Oracle 9.2 to 9.2.0.4.
28
+ return if @conn.oracle_server_version < OCI8::ORAVER_10_1
29
+
30
+ # set module
31
+ @conn.module = 'ruby-oci8'
32
+ assert_equal('ruby-oci8', @conn.select_one("SELECT SYS_CONTEXT('USERENV', 'MODULE') FROM DUAL")[0]);
33
+ # clear module
34
+ @conn.module = nil
35
+ assert_nil(@conn.select_one("SELECT SYS_CONTEXT('USERENV', 'MODULE') FROM DUAL")[0]);
36
+ end
37
+
38
+ def test_set_action
39
+ # FIXME: check again after upgrading Oracle 9.2 to 9.2.0.4.
40
+ return if @conn.oracle_server_version < OCI8::ORAVER_10_1
41
+
42
+ # set action
43
+ @conn.action = 'test_set_action'
44
+ assert_equal('test_set_action', @conn.select_one("SELECT SYS_CONTEXT('USERENV', 'ACTION') FROM DUAL")[0]);
45
+ # clear action
46
+ @conn.action = nil
47
+ assert_nil(@conn.select_one("SELECT SYS_CONTEXT('USERENV', 'ACTION') FROM DUAL")[0]);
48
+ end
49
+
50
+ def test_set_client_info
51
+ # set client_info
52
+ client_info = "ruby-oci8:#{Process.pid()}"
53
+ @conn.client_info = client_info
54
+ assert_equal(client_info, @conn.select_one("SELECT SYS_CONTEXT('USERENV', 'CLIENT_INFO') FROM DUAL")[0]);
55
+ # clear client_info
56
+ @conn.client_info = nil
57
+ assert_nil(@conn.select_one("SELECT SYS_CONTEXT('USERENV', 'CLIENT_INFO') FROM DUAL")[0]);
58
+ end
59
+
26
60
  def teardown
27
61
  @conn.logoff
28
62
  end
@@ -39,6 +39,19 @@ class TestCLob < Test::Unit::TestCase
39
39
  lob.close
40
40
  end
41
41
 
42
+ def test_insert_symbol
43
+ filename = 'test_symbol'
44
+ value = :foo_bar
45
+ @conn.exec("DELETE FROM test_clob WHERE filename = :1", filename)
46
+ @conn.exec("INSERT INTO test_clob(filename, content) VALUES (:1, EMPTY_CLOB())", filename)
47
+ cursor = @conn.exec("SELECT content FROM test_clob WHERE filename = :1 FOR UPDATE", filename)
48
+ lob = cursor.fetch[0]
49
+ lob.write(value)
50
+ lob.rewind
51
+ assert_equal(value.to_s, lob.read);
52
+ lob.close
53
+ end
54
+
42
55
  def test_read
43
56
  test_insert() # first insert data.
44
57
  filename = File.basename($lobfile)
@@ -13,6 +13,31 @@ class TestDateTime < Test::Unit::TestCase
13
13
  end
14
14
  end
15
15
 
16
+ def string_to_time(str)
17
+ /(\d+)-(\d+)-(\d+) ?(?:(\d+):(\d+):(\d+))?(?:\.(\d+))? ?([+-]\d+:\d+)?/ =~ str
18
+ args = []
19
+ args << $1.to_i # year
20
+ args << $2.to_i # month
21
+ args << $3.to_i # day
22
+ args << $4.to_i if $4 # hour
23
+ args << $5.to_i if $5 # minute
24
+ if $8
25
+ args << $6.to_i + $7.to_i.to_r / ('1' + '0' * ($7.length)).to_i
26
+ args << $8
27
+ Time.new(*args)
28
+ else
29
+ if $6
30
+ args << $6.to_i
31
+ end
32
+ if $7
33
+ args << $7.to_i.to_r * 1000000 / ('1' + '0' * ($7.length)).to_i
34
+ end
35
+ # no time zone
36
+ Time.local(*args)
37
+ end
38
+ #Time.local(*str.split(/[- :\.]/).collect do |n| n.to_i; end)
39
+ end
40
+
16
41
  def setup
17
42
  @conn = get_oci8_connection
18
43
  @local_timezone = timezone_string(*((::Time.now.utc_offset / 60).divmod 60))
@@ -129,7 +154,12 @@ EOS
129
154
  @conn.exec(<<-EOS) do |row|
130
155
  SELECT TO_TIMESTAMP_TZ('#{date}', 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM') FROM dual
131
156
  EOS
132
- assert_equal(DateTime.parse(date), row[0])
157
+ expected_val = begin
158
+ string_to_time(date)
159
+ rescue
160
+ DateTime.parse(date)
161
+ end
162
+ assert_equal(expected_val, row[0])
133
163
  end
134
164
  end
135
165
  end
@@ -274,6 +304,7 @@ EOS
274
304
  when Time
275
305
  assert_equal(tz, timezone_string(*((dt.utc_offset / 60).divmod 60)))
276
306
  when DateTime
307
+ tz = tz.gsub(/:/, '') if RUBY_VERSION <= '1.8.5'
277
308
  assert_equal(tz, dt.zone)
278
309
  else
279
310
  flunk "unexpedted type #{dt.class}"
@@ -352,9 +383,14 @@ EOS
352
383
  cursor.bind_param(:out, nil, String, 36)
353
384
  cursor.bind_param(:in1, nil, String, 36)
354
385
  cursor.bind_param(:in2, nil, :interval_ym)
355
- [['2006-01-01', -22],
356
- ['2006-01-01', -10],
386
+ [['2006-01-01', -25],
387
+ ['2006-01-01', -24],
388
+ ['2006-01-01', -23],
389
+ ['2006-01-01', -13],
390
+ ['2006-01-01', -12],
391
+ ['2006-01-01', -11],
357
392
  ['2006-01-01', +2],
393
+ ['2006-01-01', -2],
358
394
  ['2006-01-01', +12]
359
395
  ].each do |date, interval|
360
396
  cursor[:in1] = date
@@ -388,7 +424,7 @@ SELECT (TO_TIMESTAMP('#{date1}', 'YYYY-MM-DD HH24:MI:SS.FF')
388
424
  - TO_TIMESTAMP('#{date2}', 'YYYY-MM-DD HH24:MI:SS.FF')) DAY(3) TO SECOND
389
425
  FROM dual
390
426
  EOS
391
- assert_equal(DateTime.parse(date1) - DateTime.parse(date2), row[0])
427
+ assert_in_delta(string_to_time(date1) - string_to_time(date2), row[0], 0.0000000001)
392
428
  end
393
429
  end
394
430
  end
@@ -427,7 +463,7 @@ EOS
427
463
  cursor[:in1] = date1
428
464
  cursor[:in2] = date2
429
465
  cursor.exec
430
- assert_equal(DateTime.parse(date1) - DateTime.parse(date2), cursor[:out])
466
+ assert_in_delta(string_to_time(date1) - string_to_time(date2), cursor[:out], 0.0000000001)
431
467
  end
432
468
  cursor.close
433
469
  end
@@ -440,7 +476,7 @@ DECLARE
440
476
  ts1 TIMESTAMP;
441
477
  BEGIN
442
478
  ts1 := TO_TIMESTAMP(:in1, 'YYYY-MM-DD HH24:MI:SS.FF');
443
- :out := TO_CHAR(ts1 + :in2, 'YYYY-MM-DD HH24:MI:SS.FF');
479
+ :out := TO_CHAR(ts1 + :in2, 'YYYY-MM-DD HH24:MI:SS.FF6');
444
480
  END;
445
481
  EOS
446
482
  cursor.bind_param(:out, nil, String, 36)
@@ -459,11 +495,128 @@ EOS
459
495
  ['2006-01-01', +1.to_r / (24*60*60)], # one second
460
496
  ['2006-01-01', +999999.to_r / (24*60*60*1000000)] # 0.999999 seconds
461
497
  ].each do |date, interval|
498
+ interval *= 86400
462
499
  cursor[:in1] = date
463
500
  cursor[:in2] = interval
464
501
  cursor.exec
465
- assert_equal(DateTime.parse(date) + interval, DateTime.parse(cursor[:out]))
502
+ assert_equal(string_to_time(date) + interval, string_to_time(cursor[:out]))
503
+ end
504
+ cursor.close
505
+ end
506
+
507
+ def test_days_interval_ds_select
508
+ return if $oracle_version < OCI8::ORAVER_9_0
509
+
510
+ [['2006-01-01', '2004-03-01'],
511
+ ['2006-01-01', '2005-03-01'],
512
+ ['2006-01-01', '2006-03-01'],
513
+ ['2006-01-01', '2007-03-01'],
514
+ ['2006-01-01', '2006-01-01 23:00:00'],
515
+ ['2006-01-01', '2006-01-01 00:59:00'],
516
+ ['2006-01-01', '2006-01-01 00:00:59'],
517
+ ['2006-01-01', '2006-01-01 00:00:00.999999'],
518
+ ['2006-01-01', '2006-01-01 23:59:59.999999'],
519
+ ['2006-01-01', '2005-12-31 23:00:00'],
520
+ ['2006-01-01', '2005-12-31 00:59:00'],
521
+ ['2006-01-01', '2005-12-31 00:00:59'],
522
+ ['2006-01-01', '2005-12-31 00:00:00.999999'],
523
+ ['2006-01-01', '2005-12-31 23:59:59.999999']
524
+ ].each do |date1, date2|
525
+ begin
526
+ OCI8::BindType::IntervalDS.unit = :day
527
+ @conn.exec(<<-EOS) do |row|
528
+ SELECT (TO_TIMESTAMP('#{date1}', 'YYYY-MM-DD HH24:MI:SS.FF')
529
+ - TO_TIMESTAMP('#{date2}', 'YYYY-MM-DD HH24:MI:SS.FF')) DAY(3) TO SECOND
530
+ FROM dual
531
+ EOS
532
+ assert_equal(DateTime.parse(date1) - DateTime.parse(date2), row[0])
533
+ end
534
+ ensure
535
+ OCI8::BindType::IntervalDS.unit = :second
536
+ end
537
+ end
538
+ end
539
+
540
+ def test_days_interval_ds_out_bind
541
+ return if $oracle_version < OCI8::ORAVER_9_0
542
+
543
+ cursor = @conn.parse(<<-EOS)
544
+ DECLARE
545
+ ts1 TIMESTAMP;
546
+ ts2 TIMESTAMP;
547
+ BEGIN
548
+ ts1 := TO_TIMESTAMP(:in1, 'YYYY-MM-DD HH24:MI:SS.FF');
549
+ ts2 := TO_TIMESTAMP(:in2, 'YYYY-MM-DD HH24:MI:SS.FF');
550
+ :out := (ts1 - ts2) DAY TO SECOND(9);
551
+ END;
552
+ EOS
553
+ cursor.bind_param(:out, nil, :interval_ds)
554
+ cursor.bind_param(:in1, nil, String, 36)
555
+ cursor.bind_param(:in2, nil, String, 36)
556
+ [['2006-01-01', '2004-03-01'],
557
+ ['2006-01-01', '2005-03-01'],
558
+ ['2006-01-01', '2006-03-01'],
559
+ ['2006-01-01', '2007-03-01'],
560
+ ['2006-01-01', '2006-01-01 23:00:00'],
561
+ ['2006-01-01', '2006-01-01 00:59:00'],
562
+ ['2006-01-01', '2006-01-01 00:00:59'],
563
+ ['2006-01-01', '2006-01-01 00:00:00.999999'],
564
+ ['2006-01-01', '2006-01-01 23:59:59.999999'],
565
+ ['2006-01-01', '2005-12-31 23:00:00'],
566
+ ['2006-01-01', '2005-12-31 00:59:00'],
567
+ ['2006-01-01', '2005-12-31 00:00:59'],
568
+ ['2006-01-01', '2005-12-31 00:00:00.999999'],
569
+ ['2006-01-01', '2005-12-31 23:59:59.999999']
570
+ ].each do |date1, date2|
571
+ begin
572
+ OCI8::BindType::IntervalDS.unit = :day
573
+ cursor[:in1] = date1
574
+ cursor[:in2] = date2
575
+ cursor.exec
576
+ assert_equal(DateTime.parse(date1) - DateTime.parse(date2), cursor[:out])
577
+ ensure
578
+ OCI8::BindType::IntervalDS.unit = :second
579
+ end
466
580
  end
467
581
  cursor.close
468
582
  end
583
+
584
+ def test_days_interval_ds_in_bind
585
+ return if $oracle_version < OCI8::ORAVER_9_0
586
+
587
+ cursor = @conn.parse(<<-EOS)
588
+ DECLARE
589
+ ts1 TIMESTAMP;
590
+ BEGIN
591
+ ts1 := TO_TIMESTAMP(:in1, 'YYYY-MM-DD');
592
+ :out := TO_CHAR(ts1 + :in2, 'YYYY-MM-DD HH24:MI:SS.FF');
593
+ END;
594
+ EOS
595
+ cursor.bind_param(:out, nil, String, 36)
596
+ cursor.bind_param(:in1, nil, String, 36)
597
+ cursor.bind_param(:in2, nil, :interval_ds)
598
+ [['2006-01-01', -22],
599
+ ['2006-01-01', -10],
600
+ ['2006-01-01', +2],
601
+ ['2006-01-01', +12],
602
+ ['2006-01-01', -1.to_r / 24], # one hour
603
+ ['2006-01-01', -1.to_r / (24*60)], # one minute
604
+ ['2006-01-01', -1.to_r / (24*60*60)], # one second
605
+ ['2006-01-01', -999999.to_r / (24*60*60*1000000)], # 0.999999 seconds
606
+ ['2006-01-01', +1.to_r / 24], # one hour
607
+ ['2006-01-01', +1.to_r / (24*60)], # one minute
608
+ ['2006-01-01', +1.to_r / (24*60*60)], # one second
609
+ ['2006-01-01', +999999.to_r / (24*60*60*1000000)] # 0.999999 seconds
610
+ ].each do |date, interval|
611
+ begin
612
+ OCI8::BindType::IntervalDS.unit = :day
613
+ cursor[:in1] = date
614
+ cursor[:in2] = interval
615
+ cursor.exec
616
+ assert_equal(DateTime.parse(date) + interval, DateTime.parse(cursor[:out]))
617
+ ensure
618
+ OCI8::BindType::IntervalDS.unit = :second
619
+ end
620
+ end
621
+ end
469
622
  end # TestOCI8
@@ -167,6 +167,9 @@ EOS
167
167
  end
168
168
 
169
169
  def test_bind_cursor
170
+ # FIXME: check again after upgrading Oracle 9.2 to 9.2.0.4.
171
+ return if $oracle_version < OCI8::ORAVER_10_1
172
+
170
173
  drop_table('test_table')
171
174
  sql = <<-EOS
172
175
  CREATE TABLE test_table
@@ -357,12 +360,12 @@ EOS
357
360
  assert_equal(row[0], 12345678901234)
358
361
  assert_equal(row[1], 12345678901234567890)
359
362
  assert_equal(row[2], 123456789012.34)
360
- assert_equal(row[3], 1234567890123.45)
363
+ assert_equal(row[3], BigDecimal("1234567890123.45"))
361
364
  assert_equal(row[4], 1234.5)
362
- assert_instance_of(OraNumber, row[0])
365
+ assert_instance_of(BigDecimal, row[0])
363
366
  assert_instance_of(Bignum, row[1])
364
367
  assert_instance_of(Float, row[2])
365
- assert_instance_of(OraNumber, row[3])
368
+ assert_instance_of(BigDecimal, row[3])
366
369
  assert_instance_of(Float, row[4])
367
370
  end
368
371
  drop_table('test_table')
@@ -372,6 +375,8 @@ EOS
372
375
  src = [1, 1.2, BigDecimal("1.2"), Rational(12, 10)]
373
376
  int = [1, 1, 1, 1]
374
377
  flt = [1, 1.2, 1.2, 1.2]
378
+ dec = [BigDecimal("1"), BigDecimal("1.2"), BigDecimal("1.2"), BigDecimal("1.2")]
379
+ rat = [Rational(1), Rational(12, 10), Rational(12, 10), Rational(12, 10)]
375
380
 
376
381
  cursor = @conn.parse("begin :1 := :2; end;")
377
382
 
@@ -382,6 +387,7 @@ EOS
382
387
  cursor[2] = s
383
388
  cursor.exec
384
389
  assert_equal(cursor[1], flt[idx])
390
+ assert_kind_of(Float, cursor[1])
385
391
  end
386
392
 
387
393
  # Fixnum
@@ -391,6 +397,7 @@ EOS
391
397
  cursor[2] = s
392
398
  cursor.exec
393
399
  assert_equal(cursor[1], int[idx])
400
+ assert_kind_of(Fixnum, cursor[1])
394
401
  end
395
402
 
396
403
  # Integer
@@ -400,6 +407,27 @@ EOS
400
407
  cursor[2] = s
401
408
  cursor.exec
402
409
  assert_equal(cursor[1], int[idx])
410
+ assert_kind_of(Integer, cursor[1])
411
+ end
412
+
413
+ # BigDecimal
414
+ cursor.bind_param(1, nil, BigDecimal)
415
+ cursor.bind_param(2, nil, BigDecimal)
416
+ src.each_with_index do |s, idx|
417
+ cursor[2] = s
418
+ cursor.exec
419
+ assert_equal(cursor[1], dec[idx])
420
+ assert_kind_of(BigDecimal, cursor[1])
421
+ end
422
+
423
+ # Rational
424
+ cursor.bind_param(1, nil, Rational)
425
+ cursor.bind_param(2, nil, Rational)
426
+ src.each_with_index do |s, idx|
427
+ cursor[2] = s
428
+ cursor.exec
429
+ assert_equal(cursor[1], rat[idx])
430
+ assert_kind_of(Rational, cursor[1])
403
431
  end
404
432
  end
405
433