ruby-oci8 2.2.6.1 → 2.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +52 -3
- data/NEWS +106 -54
- data/dist-files +1 -1
- data/docs/install-instant-client.md +1 -0
- data/ext/oci8/apiwrap.yml +20 -0
- data/ext/oci8/bind.c +364 -3
- data/ext/oci8/lob.c +22 -29
- data/ext/oci8/object.c +37 -24
- data/ext/oci8/oci8.c +9 -11
- data/ext/oci8/oraconf.rb +2 -2
- data/ext/oci8/stmt.c +47 -12
- data/lib/oci8.rb +1 -1
- data/lib/oci8/bindtype.rb +0 -14
- data/lib/oci8/check_load_error.rb +51 -16
- data/lib/oci8/cursor.rb +46 -13
- data/lib/oci8/oci8.rb +1 -1
- data/lib/oci8/version.rb +1 -1
- data/ruby-oci8.gemspec +1 -2
- data/test/README.md +40 -0
- data/test/config.rb +1 -1
- data/test/test_oci8.rb +154 -43
- metadata +4 -6
- data/test/README +0 -42
data/lib/oci8/cursor.rb
CHANGED
@@ -25,7 +25,11 @@ class OCI8
|
|
25
25
|
@names = nil
|
26
26
|
@con = conn
|
27
27
|
@max_array_size = nil
|
28
|
+
@fetch_array_size = nil
|
29
|
+
@rowbuf_size = 0
|
30
|
+
@rowbuf_index = 0
|
28
31
|
__initialize(conn, sql) # Initialize the internal C structure.
|
32
|
+
self.prefetch_rows = conn.instance_variable_get(:@prefetch_rows)
|
29
33
|
end
|
30
34
|
|
31
35
|
# explicitly indicate the date type of fetched value. run this
|
@@ -38,7 +42,7 @@ class OCI8
|
|
38
42
|
# cursor.define(2, Time) # fetch the second column as Time.
|
39
43
|
# cursor.exec()
|
40
44
|
def define(pos, type, length = nil)
|
41
|
-
bindobj = make_bind_object(:type => type, :length => length)
|
45
|
+
bindobj = make_bind_object({:type => type, :length => length}, @fetch_array_size || 1)
|
42
46
|
__define(pos, bindobj)
|
43
47
|
if old = @define_handles[pos - 1]
|
44
48
|
old.send(:free)
|
@@ -126,6 +130,8 @@ class OCI8
|
|
126
130
|
when :select_stmt
|
127
131
|
__execute(0)
|
128
132
|
define_columns() if @column_metadata.size == 0
|
133
|
+
@rowbuf_size = 0
|
134
|
+
@rowbuf_index = 0
|
129
135
|
@column_metadata.size
|
130
136
|
else
|
131
137
|
__execute(1)
|
@@ -384,6 +390,7 @@ class OCI8
|
|
384
390
|
# @param [Integer] rows The number of rows to be prefetched
|
385
391
|
def prefetch_rows=(rows)
|
386
392
|
attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
|
393
|
+
@prefetch_rows = rows
|
387
394
|
end
|
388
395
|
|
389
396
|
if OCI8::oracle_client_version >= ORAVER_12_1
|
@@ -468,7 +475,7 @@ class OCI8
|
|
468
475
|
|
469
476
|
private
|
470
477
|
|
471
|
-
def make_bind_object(param)
|
478
|
+
def make_bind_object(param, fetch_array_size = nil)
|
472
479
|
case param
|
473
480
|
when Hash
|
474
481
|
key = param[:type]
|
@@ -510,22 +517,37 @@ class OCI8
|
|
510
517
|
OCI8::BindType::Mapping[key] = bindclass if bindclass
|
511
518
|
end
|
512
519
|
raise "unsupported datatype: #{key}" if bindclass.nil?
|
513
|
-
bindclass.create(@con, val, param, max_array_size)
|
520
|
+
bindclass.create(@con, val, param, fetch_array_size || max_array_size)
|
514
521
|
end
|
515
522
|
|
523
|
+
@@use_array_fetch = false
|
524
|
+
|
516
525
|
def define_columns
|
517
526
|
# http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5494
|
518
527
|
num_cols = attr_get_ub4(18) # OCI_ATTR_PARAM_COUNT(18)
|
519
|
-
1.upto(num_cols) do |i|
|
520
|
-
|
521
|
-
|
522
|
-
|
528
|
+
@column_metadata = 1.upto(num_cols).collect do |i|
|
529
|
+
__paramGet(i)
|
530
|
+
end
|
531
|
+
if @define_handles.size == 0
|
532
|
+
use_array_fetch = @@use_array_fetch
|
533
|
+
@column_metadata.each do |md|
|
534
|
+
case md.data_type
|
535
|
+
when :clob, :blob, :bfile
|
536
|
+
# Rows prefetching doesn't work for CLOB, BLOB and BFILE.
|
537
|
+
# Use array fetching to get more than one row in a network round trip.
|
538
|
+
use_array_fetch = true
|
539
|
+
end
|
540
|
+
end
|
541
|
+
@fetch_array_size = @prefetch_rows if use_array_fetch
|
542
|
+
end
|
543
|
+
@column_metadata.each_with_index do |md, i|
|
544
|
+
define_one_column(i + 1, md) unless @define_handles[i]
|
523
545
|
end
|
524
546
|
num_cols
|
525
547
|
end
|
526
548
|
|
527
549
|
def define_one_column(pos, param)
|
528
|
-
bindobj = make_bind_object(param)
|
550
|
+
bindobj = make_bind_object(param, @fetch_array_size || 1)
|
529
551
|
__define(pos, bindobj)
|
530
552
|
@define_handles[pos - 1] = bindobj
|
531
553
|
end
|
@@ -540,22 +562,33 @@ class OCI8
|
|
540
562
|
end
|
541
563
|
end
|
542
564
|
|
565
|
+
def fetch_row_internal
|
566
|
+
if @rowbuf_size && @rowbuf_size == @rowbuf_index
|
567
|
+
@rowbuf_size = __fetch(@con, @fetch_array_size || 1)
|
568
|
+
@rowbuf_index = 0
|
569
|
+
end
|
570
|
+
@rowbuf_size
|
571
|
+
end
|
572
|
+
|
543
573
|
def fetch_one_row_as_array
|
544
|
-
if
|
545
|
-
@define_handles.collect do |handle|
|
546
|
-
handle.send(:get_data)
|
574
|
+
if fetch_row_internal
|
575
|
+
ret = @define_handles.collect do |handle|
|
576
|
+
handle.send(:get_data, @rowbuf_index)
|
547
577
|
end
|
578
|
+
@rowbuf_index += 1
|
579
|
+
ret
|
548
580
|
else
|
549
581
|
nil
|
550
582
|
end
|
551
583
|
end
|
552
584
|
|
553
585
|
def fetch_one_row_as_hash
|
554
|
-
if
|
586
|
+
if fetch_row_internal
|
555
587
|
ret = {}
|
556
588
|
get_col_names.each_with_index do |name, idx|
|
557
|
-
ret[name] = @define_handles[idx].send(:get_data)
|
589
|
+
ret[name] = @define_handles[idx].send(:get_data, @rowbuf_index)
|
558
590
|
end
|
591
|
+
@rowbuf_index += 1
|
559
592
|
ret
|
560
593
|
else
|
561
594
|
nil
|
data/lib/oci8/oci8.rb
CHANGED
@@ -169,7 +169,6 @@ class OCI8
|
|
169
169
|
# @private
|
170
170
|
def parse_internal(sql)
|
171
171
|
cursor = OCI8::Cursor.new(self, sql)
|
172
|
-
cursor.prefetch_rows = @prefetch_rows if @prefetch_rows
|
173
172
|
cursor
|
174
173
|
end
|
175
174
|
|
@@ -305,6 +304,7 @@ class OCI8
|
|
305
304
|
# @return [Array] an array of first row.
|
306
305
|
def select_one(sql, *bindvars)
|
307
306
|
cursor = self.parse(sql)
|
307
|
+
cursor.prefetch_rows = 1
|
308
308
|
begin
|
309
309
|
cursor.exec(*bindvars)
|
310
310
|
row = cursor.fetch
|
data/lib/oci8/version.rb
CHANGED
data/ruby-oci8.gemspec
CHANGED
@@ -34,7 +34,6 @@ spec = Gem::Specification.new do |s|
|
|
34
34
|
s.description = <<EOS
|
35
35
|
ruby-oci8 is a ruby interface for Oracle using OCI8 API. It is available with Oracle 10g or later including Oracle Instant Client.
|
36
36
|
EOS
|
37
|
-
s.has_rdoc = 'yard'
|
38
37
|
s.authors = ['Kubo Takehiro']
|
39
38
|
s.platform = gem_platform
|
40
39
|
s.license = 'BSD-2-Clause'
|
@@ -79,7 +78,7 @@ EOS
|
|
79
78
|
end
|
80
79
|
files << 'lib/oci8.rb'
|
81
80
|
end
|
82
|
-
s.require_paths = ['lib'
|
81
|
+
s.require_paths = ['lib']
|
83
82
|
s.files = files
|
84
83
|
s.test_files = 'test/test_all.rb'
|
85
84
|
s.extra_rdoc_files = ['README.md']
|
data/test/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Before running unit test:
|
2
|
+
|
3
|
+
1. Connect to Oracle as sys
|
4
|
+
```shell
|
5
|
+
$ sqlplus sys/<password_of_sys> as sysdba
|
6
|
+
SQL>
|
7
|
+
```
|
8
|
+
2. Create user ruby
|
9
|
+
```sql
|
10
|
+
SQL> CREATE USER ruby IDENTIFIED BY oci8;
|
11
|
+
```
|
12
|
+
or
|
13
|
+
```sql
|
14
|
+
SQL> CREATE USER ruby IDENTIFIED BY oci8
|
15
|
+
2 DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
|
16
|
+
```
|
17
|
+
3. Grant the privilege to connect and execute.
|
18
|
+
```sql
|
19
|
+
SQL> GRANT connect, resource, create view, create synonym TO ruby;
|
20
|
+
SQL> GRANT execute ON dbms_lock TO ruby;
|
21
|
+
```
|
22
|
+
4. Connect as ruby user.
|
23
|
+
```shell
|
24
|
+
$ sqlplus ruby/oci8
|
25
|
+
SQL>
|
26
|
+
```
|
27
|
+
5. Create object types
|
28
|
+
```sql
|
29
|
+
SQL> @test/setup_test_object.sql
|
30
|
+
```
|
31
|
+
6. change $dbname in test/config.rb.
|
32
|
+
|
33
|
+
Then run the following command:
|
34
|
+
```shell
|
35
|
+
$ make check
|
36
|
+
```
|
37
|
+
or
|
38
|
+
```
|
39
|
+
$ nmake check (If your compiler is MS Visual C++.)
|
40
|
+
````
|
data/test/config.rb
CHANGED
@@ -134,7 +134,7 @@ class Minitest::Test
|
|
134
134
|
|
135
135
|
def get_oci8_connection()
|
136
136
|
OCI8.new($dbuser, $dbpass, $dbname)
|
137
|
-
|
137
|
+
rescue OCIError
|
138
138
|
raise if $!.code != 12516 && $!.code != 12520
|
139
139
|
# sleep a few second and try again if
|
140
140
|
# the error code is ORA-12516 or ORA-12520.
|
data/test/test_oci8.rb
CHANGED
@@ -25,53 +25,142 @@ EOS
|
|
25
25
|
drop_table('test_rename_table')
|
26
26
|
end
|
27
27
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
28
|
+
# Set `OCI8::BindType::Base.initial_chunk_size = 5` to
|
29
|
+
# use the following test data.
|
30
|
+
LONG_TEST_DATA = [
|
31
|
+
# initial chunk size: 5 (total buffer size: 5)
|
32
|
+
'a' * 4, 'b' * 5, 'c' * 6, 'd' * 5, 'e' * 4,
|
33
|
+
# second chunk size: 10 (total buffer size: 15)
|
34
|
+
'f' * 14, 'g' * 15, 'h' * 16, 'i' * 15, 'j' * 14,
|
35
|
+
# third chunk size: 20 (total buffer size: 35)
|
36
|
+
'k' * 34, 'l' * 35, 'm' * 36, 'n' * 35, 'o' * 34,
|
37
|
+
# use data around initial chunk size again
|
38
|
+
'p' * 4, 'q' * 5, 'r' * 6, 's' * 5, 't' * 4,
|
39
|
+
# special data
|
40
|
+
'', nil,
|
41
|
+
]
|
42
|
+
|
43
|
+
def test_long_type
|
44
|
+
clob_bind_type = OCI8::BindType::Mapping[:clob]
|
45
|
+
blob_bind_type = OCI8::BindType::Mapping[:blob]
|
46
|
+
initial_cunk_size = OCI8::BindType::Base.initial_chunk_size
|
47
|
+
begin
|
48
|
+
OCI8::BindType::Base.initial_chunk_size = 5
|
49
|
+
@conn.prefetch_rows = LONG_TEST_DATA.size / 3
|
50
|
+
drop_table('test_table')
|
51
|
+
ascii_enc = Encoding.find('US-ASCII')
|
52
|
+
0.upto(1) do |i|
|
53
|
+
if i == 0
|
54
|
+
@conn.exec("CREATE TABLE test_table (id number(38), long_column long, clob_column clob)")
|
55
|
+
cursor = @conn.parse('insert into test_table values (:1, :2, :3)')
|
56
|
+
cursor.bind_param(1, nil, Integer)
|
57
|
+
cursor.bind_param(2, nil, :long)
|
58
|
+
cursor.bind_param(3, nil, :clob)
|
59
|
+
lob = OCI8::CLOB.new(@conn, '')
|
60
|
+
enc = Encoding.default_internal || OCI8.encoding
|
61
|
+
else
|
62
|
+
@conn.exec("CREATE TABLE test_table (id number(38), long_raw_column long raw, blob_column blob)")
|
63
|
+
cursor = @conn.parse('insert into test_table values (:1, :2, :3)')
|
64
|
+
cursor.bind_param(1, nil, Integer)
|
65
|
+
cursor.bind_param(2, nil, :long_raw)
|
66
|
+
cursor.bind_param(3, nil, :blob)
|
67
|
+
lob = OCI8::BLOB.new(@conn, '')
|
68
|
+
enc = Encoding.find('ASCII-8BIT')
|
69
|
+
end
|
70
|
+
|
71
|
+
LONG_TEST_DATA.each_with_index do |data, index|
|
72
|
+
cursor[1] = index
|
73
|
+
cursor[2] = data
|
74
|
+
if data.nil?
|
75
|
+
cursor[3] = nil
|
76
|
+
else
|
77
|
+
lob.rewind
|
78
|
+
lob.write(data)
|
79
|
+
lob.size = data.size
|
80
|
+
cursor[3] = lob
|
81
|
+
end
|
82
|
+
cursor.exec
|
83
|
+
end
|
84
|
+
cursor.close
|
85
|
+
|
86
|
+
cursor = @conn.parse('SELECT * from test_table order by id')
|
87
|
+
cursor.exec
|
88
|
+
LONG_TEST_DATA.each_with_index do |data, index|
|
89
|
+
row = cursor.fetch
|
90
|
+
assert_equal(index, row[0])
|
91
|
+
if data.nil?
|
92
|
+
assert_nil(row[1])
|
93
|
+
assert_nil(row[2])
|
94
|
+
elsif data.empty?
|
95
|
+
# '' is inserted to the long or long raw column as null.
|
96
|
+
assert_nil(row[1])
|
97
|
+
# '' is inserted to the clob or blob column as an empty clob.
|
98
|
+
# It is fetched as '' when the data is read using a LOB locator.
|
99
|
+
assert_equal(data, clob_data = row[2].read)
|
100
|
+
assert_equal(ascii_enc, clob_data.encoding)
|
101
|
+
else
|
102
|
+
assert_equal(data, row[1])
|
103
|
+
assert_equal(data, clob_data = row[2].read)
|
104
|
+
assert_equal(enc, row[1].encoding)
|
105
|
+
assert_equal(enc, clob_data.encoding)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
assert_nil(cursor.fetch)
|
109
|
+
cursor.close
|
110
|
+
|
111
|
+
begin
|
112
|
+
OCI8::BindType::Mapping[:clob] = OCI8::BindType::Long
|
113
|
+
OCI8::BindType::Mapping[:blob] = OCI8::BindType::LongRaw
|
114
|
+
cursor = @conn.parse('SELECT * from test_table order by id')
|
115
|
+
cursor.exec
|
116
|
+
LONG_TEST_DATA.each_with_index do |data, index|
|
117
|
+
row = cursor.fetch
|
118
|
+
assert_equal(index, row[0])
|
119
|
+
if data.nil?
|
120
|
+
assert_nil(row[1])
|
121
|
+
assert_nil(row[2])
|
122
|
+
elsif data.empty?
|
123
|
+
# '' is inserted to the long or long raw column as null.
|
124
|
+
assert_nil(row[1])
|
125
|
+
# '' is inserted to the clob or blob column as an empty clob.
|
126
|
+
# However it is fetched as nil.
|
127
|
+
assert_nil(row[2])
|
128
|
+
else
|
129
|
+
assert_equal(data, row[1])
|
130
|
+
assert_equal(data, row[2])
|
131
|
+
assert_equal(enc, row[1].encoding)
|
132
|
+
assert_equal(enc, row[2].encoding)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
assert_nil(cursor.fetch)
|
136
|
+
cursor.close
|
137
|
+
ensure
|
138
|
+
OCI8::BindType::Mapping[:clob] = clob_bind_type
|
139
|
+
OCI8::BindType::Mapping[:blob] = blob_bind_type
|
140
|
+
end
|
141
|
+
drop_table('test_table')
|
142
|
+
end
|
143
|
+
ensure
|
144
|
+
OCI8::BindType::Base.initial_chunk_size = initial_cunk_size
|
51
145
|
end
|
52
146
|
drop_table('test_table')
|
53
147
|
end
|
54
148
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
assert_equal(test_data2, cursor.fetch[0])
|
71
|
-
assert_nil(cursor.fetch[0])
|
72
|
-
assert_equal(test_data4, cursor.fetch[0])
|
73
|
-
cursor.close
|
74
|
-
drop_table('test_table')
|
149
|
+
def test_bind_long_data
|
150
|
+
initial_cunk_size = OCI8::BindType::Base.initial_chunk_size
|
151
|
+
begin
|
152
|
+
OCI8::BindType::Base.initial_chunk_size = 5
|
153
|
+
cursor = @conn.parse("begin :1 := '<' || :2 || '>'; end;")
|
154
|
+
cursor.bind_param(1, nil, :long)
|
155
|
+
cursor.bind_param(2, nil, :long)
|
156
|
+
(LONG_TEST_DATA + ['z' * 4000]).each do |data|
|
157
|
+
cursor[2] = data
|
158
|
+
cursor.exec
|
159
|
+
assert_equal("<#{data}>", cursor[1])
|
160
|
+
end
|
161
|
+
ensure
|
162
|
+
OCI8::BindType::Base.initial_chunk_size = initial_cunk_size
|
163
|
+
end
|
75
164
|
end
|
76
165
|
|
77
166
|
def test_select
|
@@ -450,6 +539,7 @@ EOS
|
|
450
539
|
assert_nil(@conn.last_error)
|
451
540
|
@conn.last_error = 'dummy'
|
452
541
|
cursor = @conn.parse('select col1, max(col2) from (select 1 as col1, null as col2 from dual) group by col1')
|
542
|
+
cursor.prefetch_rows = 1
|
453
543
|
assert_nil(@conn.last_error)
|
454
544
|
|
455
545
|
# When an OCI function returns OCI_SUCCESS_WITH_INFO, OCI8#last_error is set.
|
@@ -510,4 +600,25 @@ EOS
|
|
510
600
|
end
|
511
601
|
assert_equal(ver, @conn.oracle_server_version.to_s)
|
512
602
|
end
|
603
|
+
|
604
|
+
def test_array_fetch
|
605
|
+
drop_table('test_table')
|
606
|
+
@conn.exec("CREATE TABLE test_table (id number, val clob)")
|
607
|
+
cursor = @conn.parse("INSERT INTO test_table VALUES (:1, :2)")
|
608
|
+
1.upto(10) do |i|
|
609
|
+
cursor.exec(i, ('a'.ord + i).chr * i)
|
610
|
+
end
|
611
|
+
cursor.close
|
612
|
+
cursor = @conn.parse("select * from test_table where id <= :1 order by id")
|
613
|
+
cursor.prefetch_rows = 4
|
614
|
+
[1, 6, 2, 7, 3, 8, 4, 9, 5, 10].each do |i|
|
615
|
+
cursor.exec(i)
|
616
|
+
1.upto(i) do |j|
|
617
|
+
row = cursor.fetch
|
618
|
+
assert_equal(j, row[0])
|
619
|
+
assert_equal(('a'.ord + j).chr * j, row[1].read)
|
620
|
+
end
|
621
|
+
assert_nil(cursor.fetch)
|
622
|
+
end
|
623
|
+
end
|
513
624
|
end # TestOCI8
|
metadata
CHANGED
@@ -5,9 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 2.2.6.1
|
8
|
+
- 7
|
9
|
+
version: 2.2.7
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Kubo Takehiro
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date:
|
17
|
+
date: 2019-01-06 00:00:00 +09:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
@@ -112,7 +111,7 @@ files:
|
|
112
111
|
- lib/oci8/properties.rb
|
113
112
|
- lib/oci8/version.rb
|
114
113
|
- lib/ruby-oci8.rb
|
115
|
-
- test/README
|
114
|
+
- test/README.md
|
116
115
|
- test/config.rb
|
117
116
|
- test/setup_test_object.sql
|
118
117
|
- test/setup_test_package.sql
|
@@ -152,7 +151,6 @@ rdoc_options: []
|
|
152
151
|
|
153
152
|
require_paths:
|
154
153
|
- lib
|
155
|
-
- ext/oci8
|
156
154
|
required_ruby_version: !ruby/object:Gem::Requirement
|
157
155
|
none: false
|
158
156
|
requirements:
|