ruby-oci8 2.0.6 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +366 -19
- data/Makefile +2 -8
- data/NEWS +111 -0
- data/README +4 -85
- data/VERSION +1 -1
- data/dist-files +9 -2
- data/ext/oci8/.document +1 -0
- data/ext/oci8/apiwrap.c.tmpl +12 -2
- data/ext/oci8/apiwrap.yml +37 -21
- data/ext/oci8/attr.c +23 -74
- data/ext/oci8/bind.c +93 -225
- data/ext/oci8/connection_pool.c +201 -0
- data/ext/oci8/encoding.c +117 -24
- data/ext/oci8/env.c +5 -10
- data/ext/oci8/error.c +171 -189
- data/ext/oci8/extconf.rb +6 -2
- data/ext/oci8/lob.c +81 -79
- data/ext/oci8/metadata.c +42 -177
- data/ext/oci8/object.c +55 -28
- data/ext/oci8/oci8.c +426 -294
- data/ext/oci8/oci8.h +84 -51
- data/ext/oci8/oci8lib.c +75 -53
- data/ext/oci8/ocidatetime.c +67 -88
- data/ext/oci8/ocihandle.c +78 -37
- data/ext/oci8/ocinumber.c +166 -109
- data/ext/oci8/oraconf.rb +68 -157
- data/ext/oci8/oradate.c +2 -7
- data/ext/oci8/stmt.c +40 -183
- data/ext/oci8/thread_util.c +85 -0
- data/ext/oci8/thread_util.h +30 -0
- data/lib/oci8.rb.in +19 -13
- data/lib/oci8/.document +2 -0
- data/lib/oci8/bindtype.rb +62 -45
- data/lib/oci8/connection_pool.rb +118 -0
- data/lib/oci8/datetime.rb +304 -320
- data/lib/oci8/encoding-init.rb +62 -30
- data/lib/oci8/encoding.yml +3 -3
- data/lib/oci8/metadata.rb +552 -497
- data/lib/oci8/object.rb +9 -9
- data/lib/oci8/oci8.rb +161 -2
- data/lib/oci8/ocihandle.rb +427 -0
- data/lib/oci8/properties.rb +31 -1
- data/ruby-oci8.gemspec +10 -3
- data/test/README +41 -3
- data/test/config.rb +16 -0
- data/test/test_all.rb +3 -0
- data/test/test_bind_string.rb +106 -0
- data/test/test_break.rb +33 -7
- data/test/test_clob.rb +13 -10
- data/test/test_connection_pool.rb +125 -0
- data/test/test_connstr.rb +2 -2
- data/test/test_datetime.rb +26 -66
- data/test/test_encoding.rb +7 -3
- data/test/test_error.rb +88 -0
- data/test/test_metadata.rb +1356 -204
- data/test/test_oci8.rb +27 -8
- data/test/test_oranumber.rb +41 -0
- metadata +34 -9
- data/ext/oci8/xmldb.c +0 -383
data/lib/oci8/properties.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# properties.rb -- implements OCI8.properties
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010 KUBO Takehiro <kubo@jiubao.org>
|
3
|
+
# Copyright (C) 2010-2011 KUBO Takehiro <kubo@jiubao.org>
|
4
4
|
|
5
5
|
class OCI8
|
6
6
|
|
7
7
|
@@properties = {
|
8
|
+
:length_semantics => :byte,
|
8
9
|
:bind_string_as_nchar => false,
|
10
|
+
:float_conversion_type => :ruby,
|
9
11
|
}
|
10
12
|
|
11
13
|
def @@properties.[](name)
|
@@ -16,8 +18,15 @@ class OCI8
|
|
16
18
|
def @@properties.[]=(name, val)
|
17
19
|
raise IndexError, "No such property name: #{name}" unless @@properties.has_key?(name)
|
18
20
|
case name
|
21
|
+
when :length_semantic
|
22
|
+
if val != :byte and val != :char
|
23
|
+
raise ArgumentError, "Invalid property value #{val} for :length_semantics."
|
24
|
+
end
|
19
25
|
when :bind_string_as_nchar
|
20
26
|
val = val ? true : false
|
27
|
+
when :float_conversion_type
|
28
|
+
# handled by native code in oci8lib_xx.so.
|
29
|
+
OCI8.__set_property(name, val)
|
21
30
|
end
|
22
31
|
super(name, val)
|
23
32
|
end
|
@@ -41,9 +50,30 @@ class OCI8
|
|
41
50
|
#
|
42
51
|
# Supported properties are listed below:
|
43
52
|
#
|
53
|
+
# [:length_semantics]
|
54
|
+
# (new in 2.1.0)
|
55
|
+
#
|
56
|
+
# +:char+ when Oracle character length is counted by the number of characters.
|
57
|
+
# +:byte+ when it is counted by the number of bytes.
|
58
|
+
# The default setting is +:byte+ because +:char+ causes unexpected behaviour on
|
59
|
+
# Oracle 9i.
|
60
|
+
#
|
44
61
|
# [:bind_string_as_nchar]
|
45
62
|
# +true+ when string bind variables are bound as NCHAR,
|
46
63
|
# otherwise +false+. The default value is +false+.
|
64
|
+
#
|
65
|
+
# [:float_conversion_type]
|
66
|
+
# (new in 2.1.0)
|
67
|
+
#
|
68
|
+
# +:ruby+ when Oracle decimal numbers are converted to ruby Float values
|
69
|
+
# same as Float#to_s does. (default)
|
70
|
+
# +:oracle:+ when they are done by Oracle OCI functions.
|
71
|
+
#
|
72
|
+
# From ruby 1.9.2, a float value converted from Oracle number 15.7 by
|
73
|
+
# the Oracle function OCINumberToReal() makes a string representation
|
74
|
+
# 15.700000000000001 by Float#to_s.
|
75
|
+
# See: http://rubyforge.org/forum/forum.php?thread_id=50030&forum_id=1078
|
76
|
+
#
|
47
77
|
def self.properties
|
48
78
|
@@properties
|
49
79
|
end
|
data/ruby-oci8.gemspec
CHANGED
@@ -8,10 +8,17 @@
|
|
8
8
|
#
|
9
9
|
require 'fileutils'
|
10
10
|
|
11
|
-
if
|
12
|
-
|
11
|
+
build_args = if (defined? Gem::Command and Gem::Command.respond_to? :build_args)
|
12
|
+
Gem::Command.build_args
|
13
|
+
else
|
14
|
+
# for old rubygems
|
15
|
+
ARGV.include?("--") ? ARGV[(ARGV.index("--") + 1)...ARGV.size] : []
|
16
|
+
end
|
17
|
+
|
18
|
+
if build_args.size > 0
|
19
|
+
gem_platform = build_args[0]
|
13
20
|
else
|
14
|
-
|
21
|
+
gem_platform = Gem::Platform::RUBY
|
15
22
|
end
|
16
23
|
|
17
24
|
spec = Gem::Specification.new do |s|
|
data/test/README
CHANGED
@@ -1,4 +1,42 @@
|
|
1
|
-
|
1
|
+
Before runing unit test:
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
1. connect to Oracle as system:
|
4
|
+
|
5
|
+
$ sqlplus system/<password_of_system>
|
6
|
+
|
7
|
+
2. create user ruby:
|
8
|
+
|
9
|
+
SQL> CREATE USER ruby IDENTIFIED BY oci8;
|
10
|
+
|
11
|
+
or
|
12
|
+
|
13
|
+
SQL> CREATE USER ruby IDENTIFIED BY oci8
|
14
|
+
2 DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
|
15
|
+
|
16
|
+
3. grant the privilege to connect and execute.
|
17
|
+
|
18
|
+
SQL> GRANT connect, resource, create view TO ruby;
|
19
|
+
|
20
|
+
4. connect to Oracle as sys
|
21
|
+
|
22
|
+
$ sqlplus 'sys/<password_of_sys> as sysdba'
|
23
|
+
|
24
|
+
5. grant privileges
|
25
|
+
|
26
|
+
SQL> GRANT EXECUTE ON dbms_lock TO ruby;
|
27
|
+
SQL> GRANT CREATE VIEW TO ruby;
|
28
|
+
|
29
|
+
6. connect as ruby user.
|
30
|
+
|
31
|
+
$ sqlplus ruby/oci8
|
32
|
+
|
33
|
+
7. Create object types
|
34
|
+
|
35
|
+
SQL> @test/setup_test_object.sql
|
36
|
+
|
37
|
+
8. change $dbname if the database
|
38
|
+
|
39
|
+
Then you can run:
|
40
|
+
$ make check
|
41
|
+
or
|
42
|
+
$ nmake check (If your compiler is MS Visual C++.)
|
data/test/config.rb
CHANGED
@@ -5,6 +5,9 @@ $dbuser = "ruby"
|
|
5
5
|
$dbpass = "oci8"
|
6
6
|
$dbname = nil
|
7
7
|
|
8
|
+
# for test_bind_string_as_nchar in test_encoding.rb
|
9
|
+
ENV['ORA_NCHAR_LITERAL_REPLACE'] = 'TRUE' if OCI8.client_charset_name.include? 'UTF8'
|
10
|
+
|
8
11
|
# test_clob.rb
|
9
12
|
|
10
13
|
nls_lang = ENV['NLS_LANG']
|
@@ -103,6 +106,19 @@ module Test
|
|
103
106
|
end
|
104
107
|
end
|
105
108
|
end # drop_table
|
109
|
+
|
110
|
+
def drop_type(type_name)
|
111
|
+
begin
|
112
|
+
@conn.exec("DROP TYPE BODY #{type_name}")
|
113
|
+
rescue OCIError
|
114
|
+
raise if $!.code != 4043
|
115
|
+
end
|
116
|
+
begin
|
117
|
+
@conn.exec("DROP TYPE #{type_name}")
|
118
|
+
rescue OCIError
|
119
|
+
raise if $!.code != 4043
|
120
|
+
end
|
121
|
+
end # drop_type
|
106
122
|
end
|
107
123
|
end
|
108
124
|
end
|
data/test/test_all.rb
CHANGED
@@ -6,6 +6,7 @@ require "#{srcdir}/config"
|
|
6
6
|
|
7
7
|
require "#{srcdir}/test_oradate"
|
8
8
|
require "#{srcdir}/test_oranumber"
|
9
|
+
require "#{srcdir}/test_bind_string"
|
9
10
|
require "#{srcdir}/test_bind_time"
|
10
11
|
require "#{srcdir}/test_bind_raw"
|
11
12
|
if $test_clob
|
@@ -21,6 +22,8 @@ require "#{srcdir}/test_array_dml"
|
|
21
22
|
require "#{srcdir}/test_rowid"
|
22
23
|
require "#{srcdir}/test_appinfo"
|
23
24
|
require "#{srcdir}/test_oracle_version"
|
25
|
+
require "#{srcdir}/test_error"
|
26
|
+
require "#{srcdir}/test_connection_pool"
|
24
27
|
|
25
28
|
if OCI8.respond_to? :encoding
|
26
29
|
require "#{srcdir}/test_encoding"
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'oci8'
|
3
|
+
require 'test/unit'
|
4
|
+
require File.dirname(__FILE__) + '/config'
|
5
|
+
|
6
|
+
class TestBindString < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@conn = get_oci8_connection
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
@conn.logoff
|
13
|
+
end
|
14
|
+
|
15
|
+
if OCI8.client_charset_name.include? 'UTF8'
|
16
|
+
|
17
|
+
def test_bind_string_as_nchar
|
18
|
+
if ['AL32UTF8', 'UTF8', 'ZHS32GB18030'].include? @conn.database_charset_name
|
19
|
+
warn "Skip test_bind_string_as_nchar. It needs Oracle server whose database chracter set is incompatible with unicode."
|
20
|
+
else
|
21
|
+
drop_table('test_table')
|
22
|
+
@conn.exec("CREATE TABLE test_table (ID NUMBER(5), V VARCHAR2(10), NV1 NVARCHAR2(10), NV2 NVARCHAR2(10))")
|
23
|
+
|
24
|
+
orig_prop = OCI8.properties[:bind_string_as_nchar]
|
25
|
+
begin
|
26
|
+
utf8_string = "a¡あ"
|
27
|
+
|
28
|
+
OCI8.properties[:bind_string_as_nchar] = false
|
29
|
+
@conn.exec("INSERT INTO test_table VALUES (1, N'#{utf8_string}', N'#{utf8_string}', :1)", utf8_string)
|
30
|
+
v, nv1, nv2 = @conn.select_one('select V, NV1, NV2 from test_table where ID = 1')
|
31
|
+
assert_not_equal(utf8_string, v) # Some UTF-8 chracters should be garbled.
|
32
|
+
assert_equal(utf8_string, nv1) # No garbled characters
|
33
|
+
assert_equal(v, nv2) # Garbled as VARCHAR2 column.
|
34
|
+
|
35
|
+
OCI8.properties[:bind_string_as_nchar] = true
|
36
|
+
@conn.exec("INSERT INTO test_table VALUES (2, N'#{utf8_string}', N'#{utf8_string}', :1)", utf8_string)
|
37
|
+
v, nv1, nv2 = @conn.select_one('select V, NV1, NV2 from test_table where ID = 2')
|
38
|
+
assert_not_equal(utf8_string, v) # Some UTF-8 chracters should be garbled.
|
39
|
+
assert_equal(utf8_string, nv1) # No garbled characters
|
40
|
+
assert_equal(nv1, nv2) # Same as NVARCHAR2.
|
41
|
+
|
42
|
+
@conn.commit
|
43
|
+
ensure
|
44
|
+
OCI8.properties[:bind_string_as_nchar] = orig_prop
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_length_semantics # This test needs to be revised.
|
50
|
+
orig_prop = OCI8.properties[:length_semantics]
|
51
|
+
begin
|
52
|
+
utf8_string = "a¡あ"
|
53
|
+
|
54
|
+
OCI8.properties[:length_semantics] = :byte
|
55
|
+
assert_equal(:byte, OCI8.properties[:length_semantics])
|
56
|
+
cursor = @conn.parse <<EOS
|
57
|
+
DECLARE
|
58
|
+
TMP VARCHAR2(6);
|
59
|
+
BEGIN
|
60
|
+
TMP := :in;
|
61
|
+
:out := TMP;
|
62
|
+
END;
|
63
|
+
EOS
|
64
|
+
cursor.bind_param(:in, utf8_string)
|
65
|
+
cursor.bind_param(:out, nil, String, 5)
|
66
|
+
begin
|
67
|
+
cursor.exec
|
68
|
+
rescue OCIError
|
69
|
+
assert_equal(6502, $!.code)
|
70
|
+
end
|
71
|
+
cursor.bind_param(:out, nil, String, 6)
|
72
|
+
cursor.exec
|
73
|
+
assert_equal(utf8_string, cursor[:out])
|
74
|
+
|
75
|
+
OCI8.properties[:length_semantics] = :char
|
76
|
+
assert_equal(:char, OCI8.properties[:length_semantics])
|
77
|
+
cursor = @conn.parse <<EOS
|
78
|
+
DECLARE
|
79
|
+
TMP VARCHAR2(6);
|
80
|
+
BEGIN
|
81
|
+
TMP := :in;
|
82
|
+
:out := TMP;
|
83
|
+
END;
|
84
|
+
EOS
|
85
|
+
cursor.bind_param(:in, utf8_string, String, 3)
|
86
|
+
cursor.bind_param(:out, nil, String, 2)
|
87
|
+
begin
|
88
|
+
cursor.exec
|
89
|
+
rescue OCIError
|
90
|
+
assert_equal(6502, $!.code)
|
91
|
+
end
|
92
|
+
cursor.bind_param(:out, nil, String, 3)
|
93
|
+
cursor.exec
|
94
|
+
assert_equal(utf8_string, cursor[:out])
|
95
|
+
ensure
|
96
|
+
OCI8.properties[:length_semantics] = orig_prop
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
else
|
101
|
+
|
102
|
+
def test_dummy
|
103
|
+
# to suppress "No tests were specified."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/test/test_break.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# High-level API
|
2
2
|
require 'oci8'
|
3
3
|
require 'test/unit'
|
4
|
+
require 'timeout'
|
4
5
|
require File.dirname(__FILE__) + '/config'
|
5
6
|
|
6
7
|
class TestBreak < Test::Unit::TestCase
|
@@ -16,7 +17,18 @@ class TestBreak < Test::Unit::TestCase
|
|
16
17
|
def report(str)
|
17
18
|
printf "%d: %s\n", (Time.now - $start_time), str
|
18
19
|
end
|
19
|
-
|
20
|
+
|
21
|
+
@@server_is_runing_on_windows = nil
|
22
|
+
def server_is_runing_on_windows?
|
23
|
+
if @@server_is_runing_on_windows.nil?
|
24
|
+
@@server_is_runing_on_windows = false
|
25
|
+
@conn.exec('select banner from v$version') do |row|
|
26
|
+
@@server_is_runing_on_windows = true if row[0].include? 'Windows'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@@server_is_runing_on_windows
|
30
|
+
end
|
31
|
+
|
20
32
|
PLSQL_DONE = 1
|
21
33
|
OCIBREAK = 2
|
22
34
|
SEND_BREAK = 3
|
@@ -33,6 +45,7 @@ class TestBreak < Test::Unit::TestCase
|
|
33
45
|
assert_equal(expect[PLSQL_DONE], (Time.now - $start_time).round, 'PLSQL_DONE')
|
34
46
|
rescue OCIBreak
|
35
47
|
assert_equal(expect[OCIBREAK], (Time.now - $start_time).round, 'OCIBREAK')
|
48
|
+
assert_equal('Canceled by user request.', $!.to_s)
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
@@ -54,15 +67,10 @@ class TestBreak < Test::Unit::TestCase
|
|
54
67
|
end
|
55
68
|
|
56
69
|
def test_non_blocking_mode
|
57
|
-
is_windows_server = false
|
58
|
-
@conn.exec('select banner from v$version') do |row|
|
59
|
-
is_windows_server = true if row[0].include? 'Windows'
|
60
|
-
end
|
61
|
-
|
62
70
|
@conn.non_blocking = true
|
63
71
|
assert_equal(true, @conn.non_blocking?)
|
64
72
|
expect = []
|
65
|
-
if
|
73
|
+
if server_is_runing_on_windows?
|
66
74
|
if $oracle_server_version >= OCI8::ORAVER_9_0
|
67
75
|
# raise after sleeping #{TIME_IN_PLSQL} seconds.
|
68
76
|
expect[PLSQL_DONE] = "Invalid status"
|
@@ -79,4 +87,22 @@ class TestBreak < Test::Unit::TestCase
|
|
79
87
|
expect[SEND_BREAK] = TIME_TO_BREAK
|
80
88
|
do_test_ocibreak(@conn, expect)
|
81
89
|
end
|
90
|
+
|
91
|
+
def test_timeout
|
92
|
+
@conn.non_blocking = true
|
93
|
+
start_time = Time.now
|
94
|
+
assert_raise(Timeout::Error) do
|
95
|
+
Timeout.timeout(1) do
|
96
|
+
@conn.exec("BEGIN DBMS_LOCK.SLEEP(5); END;")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
if server_is_runing_on_windows?
|
100
|
+
end_time = start_time + 5
|
101
|
+
else
|
102
|
+
end_time = start_time + 1
|
103
|
+
end
|
104
|
+
assert_in_delta(Time.now, end_time, 1)
|
105
|
+
@conn.exec("BEGIN NULL; END;")
|
106
|
+
assert_in_delta(Time.now, end_time, 1)
|
107
|
+
end
|
82
108
|
end
|
data/test/test_clob.rb
CHANGED
@@ -7,13 +7,15 @@ class TestCLob < Test::Unit::TestCase
|
|
7
7
|
|
8
8
|
def setup
|
9
9
|
@conn = get_oci8_connection
|
10
|
+
drop_table('test_table')
|
11
|
+
@conn.exec('CREATE TABLE test_table (filename VARCHAR2(40), content CLOB)')
|
10
12
|
end
|
11
13
|
|
12
14
|
def test_insert
|
13
15
|
filename = File.basename($lobfile)
|
14
|
-
@conn.exec("DELETE FROM
|
15
|
-
@conn.exec("INSERT INTO
|
16
|
-
cursor = @conn.exec("SELECT content FROM
|
16
|
+
@conn.exec("DELETE FROM test_table WHERE filename = :1", filename)
|
17
|
+
@conn.exec("INSERT INTO test_table(filename, content) VALUES (:1, EMPTY_CLOB())", filename)
|
18
|
+
cursor = @conn.exec("SELECT content FROM test_table WHERE filename = :1 FOR UPDATE", filename)
|
17
19
|
lob = cursor.fetch[0]
|
18
20
|
open($lobfile) do |f|
|
19
21
|
while f.gets()
|
@@ -25,9 +27,9 @@ class TestCLob < Test::Unit::TestCase
|
|
25
27
|
|
26
28
|
def test_insert_with_flush
|
27
29
|
filename = File.basename($lobfile)
|
28
|
-
@conn.exec("DELETE FROM
|
29
|
-
@conn.exec("INSERT INTO
|
30
|
-
cursor = @conn.exec("SELECT content FROM
|
30
|
+
@conn.exec("DELETE FROM test_table WHERE filename = :1", filename)
|
31
|
+
@conn.exec("INSERT INTO test_table(filename, content) VALUES (:1, EMPTY_CLOB())", filename)
|
32
|
+
cursor = @conn.exec("SELECT content FROM test_table WHERE filename = :1 FOR UPDATE", filename)
|
31
33
|
lob = cursor.fetch[0]
|
32
34
|
lob.sync = false
|
33
35
|
open($lobfile) do |f|
|
@@ -42,9 +44,9 @@ class TestCLob < Test::Unit::TestCase
|
|
42
44
|
def test_insert_symbol
|
43
45
|
filename = 'test_symbol'
|
44
46
|
value = :foo_bar
|
45
|
-
@conn.exec("DELETE FROM
|
46
|
-
@conn.exec("INSERT INTO
|
47
|
-
cursor = @conn.exec("SELECT content FROM
|
47
|
+
@conn.exec("DELETE FROM test_table WHERE filename = :1", filename)
|
48
|
+
@conn.exec("INSERT INTO test_table(filename, content) VALUES (:1, EMPTY_CLOB())", filename)
|
49
|
+
cursor = @conn.exec("SELECT content FROM test_table WHERE filename = :1 FOR UPDATE", filename)
|
48
50
|
lob = cursor.fetch[0]
|
49
51
|
lob.write(value)
|
50
52
|
lob.rewind
|
@@ -55,7 +57,7 @@ class TestCLob < Test::Unit::TestCase
|
|
55
57
|
def test_read
|
56
58
|
test_insert() # first insert data.
|
57
59
|
filename = File.basename($lobfile)
|
58
|
-
cursor = @conn.exec("SELECT content FROM
|
60
|
+
cursor = @conn.exec("SELECT content FROM test_table WHERE filename = :1 FOR UPDATE", filename)
|
59
61
|
lob = cursor.fetch[0]
|
60
62
|
|
61
63
|
open($lobfile) do |f|
|
@@ -74,6 +76,7 @@ class TestCLob < Test::Unit::TestCase
|
|
74
76
|
end
|
75
77
|
|
76
78
|
def teardown
|
79
|
+
drop_table('test_table')
|
77
80
|
@conn.logoff
|
78
81
|
end
|
79
82
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'oci8'
|
2
|
+
require 'test/unit'
|
3
|
+
require File.dirname(__FILE__) + '/config'
|
4
|
+
|
5
|
+
class TestConnectionPool < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def create_pool(min, max, incr)
|
8
|
+
OCI8::ConnectionPool.new(min, max, incr, $dbuser, $dbpass, $dbname)
|
9
|
+
rescue OCIError
|
10
|
+
raise if $!.code != 12516 && $!.code != 12520
|
11
|
+
sleep(5)
|
12
|
+
OCI8::ConnectionPool.new(min, max, incr, $dbuser, $dbpass, $dbname)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_connect
|
16
|
+
pool = create_pool(1, 5, 3)
|
17
|
+
assert_equal(1, pool.min)
|
18
|
+
assert_equal(5, pool.max)
|
19
|
+
assert_equal(3, pool.incr)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_reinitialize
|
23
|
+
pool = create_pool(1, 5, 3)
|
24
|
+
pool.reinitialize(2, 6, 4)
|
25
|
+
assert_equal(2, pool.min)
|
26
|
+
assert_equal(6, pool.max)
|
27
|
+
assert_equal(4, pool.incr)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_busy_and_open_count
|
31
|
+
check_busy_and_open_count(1, 5, 3)
|
32
|
+
check_busy_and_open_count(2, 4, 1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_busy_and_open_count(min_cnt, max_cnt, incr_cnt)
|
36
|
+
msg = "create_pool(#{min_cnt}, #{max_cnt}, #{incr_cnt})"
|
37
|
+
# Create a connection pool.
|
38
|
+
pool = create_pool(min_cnt, max_cnt, incr_cnt)
|
39
|
+
assert_equal(min_cnt, pool.open_count, msg)
|
40
|
+
assert_equal(0, pool.busy_count, msg)
|
41
|
+
|
42
|
+
non_blocking = true
|
43
|
+
|
44
|
+
# Create connections from the pool.
|
45
|
+
conns = []
|
46
|
+
max_cnt.times do |cnt|
|
47
|
+
conn = OCI8.new($dbuser, $dbpass, pool)
|
48
|
+
if cnt == 0
|
49
|
+
unless conn.non_blocking?
|
50
|
+
non_blocking = false
|
51
|
+
assert_raise(RuntimeError) do
|
52
|
+
# This should raise "Could not set non-blocking mode to a connection allocated from OCI8::ConnectionPool."
|
53
|
+
conn.non_blocking = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
conns << conn
|
58
|
+
end
|
59
|
+
assert_equal(min_cnt, pool.open_count, msg)
|
60
|
+
assert_equal(0, pool.busy_count, msg)
|
61
|
+
|
62
|
+
# Execute blocking SQL statements sequentially.
|
63
|
+
max_cnt.times do |n|
|
64
|
+
thread = Thread.start do
|
65
|
+
conns[n].exec "BEGIN DBMS_LOCK.SLEEP(1); END;"
|
66
|
+
end
|
67
|
+
sleep(0.5)
|
68
|
+
assert_equal(min_cnt, pool.open_count, msg)
|
69
|
+
assert_equal(non_blocking ? 1 : 0, pool.busy_count, msg)
|
70
|
+
thread.join
|
71
|
+
end
|
72
|
+
assert_equal(min_cnt, pool.open_count, msg)
|
73
|
+
assert_equal(0, pool.busy_count, msg)
|
74
|
+
|
75
|
+
# Execute blocking SQL statements parallel to increment open_count.
|
76
|
+
threads = []
|
77
|
+
(min_cnt + 1).times do |n|
|
78
|
+
threads << Thread.start do
|
79
|
+
conns[n].exec "BEGIN DBMS_LOCK.SLEEP(2); END;"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
sleep(0.5)
|
83
|
+
assert_equal(non_blocking ? (min_cnt + incr_cnt) : min_cnt, pool.open_count, msg)
|
84
|
+
assert_equal(non_blocking ? (min_cnt + 1) : 0, pool.busy_count, msg)
|
85
|
+
|
86
|
+
# Execute blocking SQL statements parallel up to maximum.
|
87
|
+
(min_cnt + 1).upto(max_cnt - 1) do |n|
|
88
|
+
threads << Thread.start do
|
89
|
+
conns[n].exec "BEGIN DBMS_LOCK.SLEEP(1); END;"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
sleep(0.5)
|
93
|
+
assert_equal(non_blocking ? max_cnt : min_cnt, pool.open_count, msg)
|
94
|
+
assert_equal(non_blocking ? max_cnt : 0, pool.busy_count, msg)
|
95
|
+
|
96
|
+
#
|
97
|
+
threads.each do |thr|
|
98
|
+
thr.join
|
99
|
+
end
|
100
|
+
assert_equal(non_blocking ? max_cnt : min_cnt, pool.open_count, msg)
|
101
|
+
assert_equal(0, pool.busy_count, msg)
|
102
|
+
|
103
|
+
# Set timeout
|
104
|
+
pool.timeout = 1
|
105
|
+
sleep(1.5)
|
106
|
+
assert_equal(non_blocking ? max_cnt : min_cnt, pool.open_count, msg) # open_count doesn't shrink.
|
107
|
+
assert_equal(0, pool.busy_count, msg)
|
108
|
+
conns[0].ping # make a network roundtrip.
|
109
|
+
sleep(0.5)
|
110
|
+
# open_count shrinks.
|
111
|
+
# The decrement count depends on Oracle version.
|
112
|
+
assert_operator(pool.open_count, :<, max_cnt, msg)
|
113
|
+
assert_equal(0, pool.busy_count, msg)
|
114
|
+
|
115
|
+
# Close all conections.
|
116
|
+
conns.each do | conn |
|
117
|
+
conn.logoff
|
118
|
+
end
|
119
|
+
assert_equal(min_cnt, pool.open_count, msg)
|
120
|
+
assert_equal(0, pool.busy_count, msg)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_nowait
|
124
|
+
end
|
125
|
+
end
|