ruby-oci8 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +569 -0
- data/Makefile +51 -0
- data/NEWS +322 -0
- data/README +415 -0
- data/VERSION +1 -0
- data/dist-files +70 -0
- data/doc/api.en.html +527 -0
- data/doc/api.en.rd +554 -0
- data/doc/api.ja.html +525 -0
- data/doc/api.ja.rd +557 -0
- data/doc/manual.css +35 -0
- data/ext/oci8/MANIFEST +22 -0
- data/ext/oci8/attr.c +415 -0
- data/ext/oci8/bind.c +194 -0
- data/ext/oci8/const.c +165 -0
- data/ext/oci8/define.c +53 -0
- data/ext/oci8/describe.c +81 -0
- data/ext/oci8/descriptor.c +39 -0
- data/ext/oci8/env.c +276 -0
- data/ext/oci8/error.c +234 -0
- data/ext/oci8/extconf.rb +118 -0
- data/ext/oci8/handle.c +262 -0
- data/ext/oci8/lob.c +386 -0
- data/ext/oci8/oci8.c +137 -0
- data/ext/oci8/oci8.h +345 -0
- data/ext/oci8/ocinumber.c +117 -0
- data/ext/oci8/oraconf.rb +1026 -0
- data/ext/oci8/oradate.c +426 -0
- data/ext/oci8/oranumber.c +445 -0
- data/ext/oci8/param.c +37 -0
- data/ext/oci8/post-config.rb +5 -0
- data/ext/oci8/server.c +182 -0
- data/ext/oci8/session.c +99 -0
- data/ext/oci8/stmt.c +624 -0
- data/ext/oci8/svcctx.c +229 -0
- data/lib/DBD/OCI8/OCI8.rb +549 -0
- data/lib/oci8.rb.in +1605 -0
- data/metaconfig +142 -0
- data/pre-distclean.rb +7 -0
- data/ruby-oci8.gemspec +54 -0
- data/ruby-oci8.spec +62 -0
- data/setup.rb +1331 -0
- data/support/README +4 -0
- data/support/runit/assert.rb +281 -0
- data/support/runit/cui/testrunner.rb +101 -0
- data/support/runit/error.rb +4 -0
- data/support/runit/method_mappable.rb +20 -0
- data/support/runit/robserver.rb +25 -0
- data/support/runit/setuppable.rb +15 -0
- data/support/runit/teardownable.rb +16 -0
- data/support/runit/testcase.rb +113 -0
- data/support/runit/testfailure.rb +25 -0
- data/support/runit/testresult.rb +121 -0
- data/support/runit/testsuite.rb +43 -0
- data/support/runit/version.rb +3 -0
- data/test/README +4 -0
- data/test/config.rb +129 -0
- data/test/test_all.rb +43 -0
- data/test/test_bind_raw.rb +53 -0
- data/test/test_bind_time.rb +191 -0
- data/test/test_break.rb +81 -0
- data/test/test_clob.rb +101 -0
- data/test/test_connstr.rb +80 -0
- data/test/test_dbi.rb +317 -0
- data/test/test_dbi_clob.rb +56 -0
- data/test/test_describe.rb +137 -0
- data/test/test_metadata.rb +243 -0
- data/test/test_oci8.rb +273 -0
- data/test/test_oradate.rb +263 -0
- data/test/test_oranumber.rb +149 -0
- metadata +118 -0
data/ext/oci8/svcctx.c
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
/*
|
2
|
+
svcctx.c - part of ruby-oci8
|
3
|
+
|
4
|
+
Copyright (C) 2002 KUBO Takehiro <kubo@jiubao.org>
|
5
|
+
|
6
|
+
=begin
|
7
|
+
== OCISvcCtx
|
8
|
+
The service context handle is correspond to `session' compared
|
9
|
+
with other general database interfaces although OCI constains OCISession.
|
10
|
+
|
11
|
+
This handle cooperates with a ((<server handle|OCIServer>)), a
|
12
|
+
((<user session handle|OCISession>)), and a transaction handle.
|
13
|
+
But these three handles work at the back of it. So you don't have to use
|
14
|
+
them except when you have special purpose.
|
15
|
+
|
16
|
+
super class: ((<OCIHandle>))
|
17
|
+
|
18
|
+
correspond native OCI datatype: ((|OCISvcCtx|))
|
19
|
+
=end
|
20
|
+
*/
|
21
|
+
#include "oci8.h"
|
22
|
+
|
23
|
+
/*
|
24
|
+
=begin
|
25
|
+
--- OCISvcCtx#logoff()
|
26
|
+
disconnect from Oracle.
|
27
|
+
|
28
|
+
If you use ((<OCIServer#attach>)) and ((<OCISession#begin>)) to logon,
|
29
|
+
use ((<OCIServer#detach>)) and ((<OCISession#end>)) instead.
|
30
|
+
See also ((<Simplified Logon>)) and ((<Explicit Attach and Begin Session>)).
|
31
|
+
|
32
|
+
correspond native OCI function: ((|OCILogoff|))
|
33
|
+
=end
|
34
|
+
*/
|
35
|
+
static VALUE oci8_svcctx_logoff(VALUE self)
|
36
|
+
{
|
37
|
+
oci8_handle_t *h;
|
38
|
+
sword rv;
|
39
|
+
|
40
|
+
Get_Handle(self, h); /* 0 */
|
41
|
+
|
42
|
+
rv = OCILogoff(h->hp, h->errhp);
|
43
|
+
if (rv != OCI_SUCCESS)
|
44
|
+
oci8_raise(h->errhp, rv, NULL);
|
45
|
+
return self;
|
46
|
+
}
|
47
|
+
|
48
|
+
/*
|
49
|
+
=begin
|
50
|
+
--- OCISvcCtx#passwordChange(username, old_password, new_password [, mode])
|
51
|
+
:username
|
52
|
+
the username.
|
53
|
+
:old_password
|
54
|
+
old password of the user.
|
55
|
+
:new_password
|
56
|
+
new password of the user.
|
57
|
+
:mode
|
58
|
+
((|OCI_DEFAULT|)) or ((|OCI_AUTH|)). Default value is ((|OCI_DEFAULT|)).
|
59
|
+
|
60
|
+
For most cases, use default value. If you want to know detail,
|
61
|
+
see "Oracle Call Interface Programmer's Guide".
|
62
|
+
|
63
|
+
correspond native OCI function: ((|OCIPasswordChange|))
|
64
|
+
=end
|
65
|
+
*/
|
66
|
+
static VALUE oci8_password_change(int argc, VALUE *argv, VALUE self)
|
67
|
+
{
|
68
|
+
VALUE vusername, vopasswd, vnpasswd, vmode;
|
69
|
+
oci8_handle_t *h;
|
70
|
+
oci8_string_t username, opasswd, npasswd;
|
71
|
+
ub4 mode;
|
72
|
+
sword rv;
|
73
|
+
|
74
|
+
rb_scan_args(argc, argv, "31", &vusername, &vopasswd, &vnpasswd, &vmode);
|
75
|
+
Get_Handle(self, h); /* 0 */
|
76
|
+
Get_String(vusername, username); /* 1 */
|
77
|
+
Get_String(vopasswd, opasswd); /* 2 */
|
78
|
+
Get_String(vnpasswd, npasswd); /* 3 */
|
79
|
+
Get_Int_With_Default(argc, 4, vmode, mode, OCI_DEFAULT); /* 4 */
|
80
|
+
|
81
|
+
rv = OCIPasswordChange(h->hp, h->errhp, username.ptr, username.len,
|
82
|
+
opasswd.ptr, opasswd.len, npasswd.ptr, npasswd.len, mode);
|
83
|
+
if (rv != OCI_SUCCESS) {
|
84
|
+
oci8_raise(h->errhp, rv, NULL);
|
85
|
+
}
|
86
|
+
return self;
|
87
|
+
}
|
88
|
+
|
89
|
+
/*
|
90
|
+
=begin
|
91
|
+
--- OCISvcCtx#commit([flags])
|
92
|
+
commit the transaction.
|
93
|
+
|
94
|
+
:flags
|
95
|
+
((|OCI_DEFAULT|)) or ((|OCI_TRANS_TWOPHASE|)).
|
96
|
+
Default value is ((|OCI_DEFAULT|)).
|
97
|
+
|
98
|
+
correspond native OCI function: ((|OCITransCommit|))
|
99
|
+
=end
|
100
|
+
*/
|
101
|
+
static VALUE oci8_trans_commit(int argc, VALUE *argv, VALUE self)
|
102
|
+
{
|
103
|
+
VALUE vflags;
|
104
|
+
oci8_handle_t *h;
|
105
|
+
ub4 flags;
|
106
|
+
sword rv;
|
107
|
+
|
108
|
+
rb_scan_args(argc, argv, "01", &vflags);
|
109
|
+
Get_Handle(self, h); /* 0 */
|
110
|
+
Get_Int_With_Default(argc, 1, vflags, flags, OCI_DEFAULT); /* 1 */
|
111
|
+
|
112
|
+
rv = OCITransCommit(h->hp, h->errhp, flags);
|
113
|
+
if (rv != OCI_SUCCESS) {
|
114
|
+
oci8_raise(h->errhp, rv, NULL);
|
115
|
+
}
|
116
|
+
return self;
|
117
|
+
}
|
118
|
+
|
119
|
+
/*
|
120
|
+
=begin
|
121
|
+
|
122
|
+
--- OCISvcCtx#rollback([flags])
|
123
|
+
rollback the transaction.
|
124
|
+
|
125
|
+
:flags
|
126
|
+
((|OCI_DEFAULT|)) only valid. Default value is ((|OCI_DEFAULT|)).
|
127
|
+
|
128
|
+
correspond native OCI function: ((|OCITransRollback|))
|
129
|
+
=end
|
130
|
+
*/
|
131
|
+
static VALUE oci8_trans_rollback(int argc, VALUE *argv, VALUE self)
|
132
|
+
{
|
133
|
+
VALUE vflags;
|
134
|
+
oci8_handle_t *h;
|
135
|
+
ub4 flags;
|
136
|
+
sword rv;
|
137
|
+
|
138
|
+
rb_scan_args(argc, argv, "01", &vflags);
|
139
|
+
Get_Handle(self, h); /* 0 */
|
140
|
+
Get_Int_With_Default(argc, 1, vflags, flags, OCI_DEFAULT); /* 1 */
|
141
|
+
|
142
|
+
rv = OCITransRollback(h->hp, h->errhp, flags);
|
143
|
+
if (rv != OCI_SUCCESS) {
|
144
|
+
oci8_raise(h->errhp, rv, NULL);
|
145
|
+
}
|
146
|
+
return self;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* THIS WILL BE DELETED IN FUTURE RELEASE. */
|
150
|
+
static VALUE oci8_describe_any(VALUE self, VALUE vdsc, VALUE vname, VALUE vtype)
|
151
|
+
{
|
152
|
+
oci8_handle_t *h;
|
153
|
+
oci8_handle_t *dsch;
|
154
|
+
oci8_string_t name;
|
155
|
+
ub1 type;
|
156
|
+
sword rv;
|
157
|
+
|
158
|
+
Get_Handle(self, h); /* 0 */
|
159
|
+
Check_Handle(vdsc, OCIDescribe, dsch); /* 1 */
|
160
|
+
Get_String(vname, name); /* 2 */
|
161
|
+
type = FIX2INT(vtype); /* 3 */
|
162
|
+
|
163
|
+
rv = OCIDescribeAny(h->hp, h->errhp, name.ptr, name.len, OCI_OTYPE_NAME, OCI_DEFAULT, type, dsch->hp);
|
164
|
+
if (rv != OCI_SUCCESS) {
|
165
|
+
oci8_raise(h->errhp, rv, NULL);
|
166
|
+
}
|
167
|
+
return self;
|
168
|
+
}
|
169
|
+
|
170
|
+
static VALUE oci8_close_all_files(VALUE self)
|
171
|
+
{
|
172
|
+
oci8_handle_t *h;
|
173
|
+
sword rv;
|
174
|
+
|
175
|
+
Get_Handle(self, h); /* 0 */
|
176
|
+
rv = OCILobFileCloseAll(h->hp, h->errhp);
|
177
|
+
if (rv != OCI_SUCCESS) {
|
178
|
+
oci8_raise(h->errhp, rv, NULL);
|
179
|
+
}
|
180
|
+
return self;
|
181
|
+
}
|
182
|
+
|
183
|
+
void Init_oci8_svcctx(void)
|
184
|
+
{
|
185
|
+
rb_define_method(cOCISvcCtx, "logoff", oci8_svcctx_logoff, 0);
|
186
|
+
rb_define_method(cOCISvcCtx, "passwordChange", oci8_password_change, -1);
|
187
|
+
rb_define_method(cOCISvcCtx, "commit", oci8_trans_commit, -1);
|
188
|
+
rb_define_method(cOCISvcCtx, "rollback", oci8_trans_rollback, -1);
|
189
|
+
rb_define_method(cOCISvcCtx, "describeAny", oci8_describe_any, 3); /* delete later. */
|
190
|
+
rb_define_method(cOCISvcCtx, "version", oci8_server_version, 0);
|
191
|
+
#ifdef HAVE_OCISERVERRELEASE
|
192
|
+
rb_define_method(cOCISvcCtx, "release", oci8_server_release, 0);
|
193
|
+
#endif
|
194
|
+
rb_define_method(cOCISvcCtx, "break", oci8_break, 0);
|
195
|
+
#ifdef HAVE_OCIRESET
|
196
|
+
rb_define_method(cOCISvcCtx, "reset", oci8_reset, 0);
|
197
|
+
#endif
|
198
|
+
rb_define_method(cOCISvcCtx, "close_all_files", oci8_close_all_files, 0);
|
199
|
+
}
|
200
|
+
|
201
|
+
/*
|
202
|
+
=begin
|
203
|
+
--- OCISvcCtx#version()
|
204
|
+
get server version.
|
205
|
+
|
206
|
+
:return value
|
207
|
+
string of server version. For example
|
208
|
+
Oracle8 Release 8.0.5.0.0 - Production
|
209
|
+
PL/SQL Release 8.0.5.0.0 - Production
|
210
|
+
|
211
|
+
correspond native OCI function: ((|OCIServerVersion|))
|
212
|
+
|
213
|
+
--- OCISvcCtx#release()
|
214
|
+
get server version number and string
|
215
|
+
|
216
|
+
:return value
|
217
|
+
array of number and string. For example
|
218
|
+
|
219
|
+
version_number, version_str = svc.release()
|
220
|
+
version_number is 0x8005000.
|
221
|
+
version_str is
|
222
|
+
Oracle8 Release 8.0.5.0.0 - Production
|
223
|
+
PL/SQL Release 8.0.5.0.0 - Production
|
224
|
+
|
225
|
+
correspond native OCI function: ((|OCIServerVersion|))
|
226
|
+
|
227
|
+
Oracle 9i or later?
|
228
|
+
=end
|
229
|
+
*/
|
@@ -0,0 +1,549 @@
|
|
1
|
+
#
|
2
|
+
# DBD::OCI8
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2007 KUBO Takehiro <kubo@jiubao.org>
|
5
|
+
#
|
6
|
+
# copied some code from DBD::Oracle.
|
7
|
+
# DBD::Oracle's copyright is as follows:
|
8
|
+
# --------------------- begin -------------------
|
9
|
+
#
|
10
|
+
# Copyright (c) 2001, 2002, 2003, 2004 Michael Neumann <mneumann@ntecs.de>
|
11
|
+
#
|
12
|
+
# All rights reserved.
|
13
|
+
#
|
14
|
+
# Redistribution and use in source and binary forms, with or without
|
15
|
+
# modification, are permitted provided that the following conditions
|
16
|
+
# are met:
|
17
|
+
# 1. Redistributions of source code must retain the above copyright
|
18
|
+
# notice, this list of conditions and the following disclaimer.
|
19
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
20
|
+
# notice, this list of conditions and the following disclaimer in the
|
21
|
+
# documentation and/or other materials provided with the distribution.
|
22
|
+
# 3. The name of the author may not be used to endorse or promote products
|
23
|
+
# derived from this software without specific prior written permission.
|
24
|
+
#
|
25
|
+
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
26
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
27
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
28
|
+
# THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
29
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
30
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
31
|
+
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
32
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
33
|
+
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
34
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
35
|
+
#
|
36
|
+
# --------------------- end -------------------
|
37
|
+
|
38
|
+
require 'oci8'
|
39
|
+
|
40
|
+
module DBI # :nodoc:
|
41
|
+
module DBD # :nodoc:
|
42
|
+
module OCI8
|
43
|
+
|
44
|
+
VERSION = "0.1"
|
45
|
+
USED_DBD_VERSION = "0.2"
|
46
|
+
|
47
|
+
module Util
|
48
|
+
|
49
|
+
ERROR_MAP = {
|
50
|
+
1 => DBI::IntegrityError, # unique constraint violated
|
51
|
+
900 => DBI::ProgrammingError, # invalid SQL statement
|
52
|
+
904 => DBI::ProgrammingError, # invalid identifier
|
53
|
+
905 => DBI::ProgrammingError, # missing keyword
|
54
|
+
923 => DBI::ProgrammingError, # FROM keyword not found where expected
|
55
|
+
936 => DBI::ProgrammingError, # missing expression
|
56
|
+
942 => DBI::ProgrammingError, # table or view does not exist
|
57
|
+
2290 => DBI::IntegrityError, # check constraint violated
|
58
|
+
2291 => DBI::IntegrityError, # parent key not found
|
59
|
+
2292 => DBI::IntegrityError, # child record found
|
60
|
+
2293 => DBI::IntegrityError, # check constraint violated
|
61
|
+
}
|
62
|
+
|
63
|
+
def raise_dbierror(err) # :nodoc:
|
64
|
+
if err.is_a? OCIError
|
65
|
+
exc = ERROR_MAP[err.code] || DBI::DatabaseError
|
66
|
+
raise exc.new(err.message, err.code)
|
67
|
+
else
|
68
|
+
raise DBI::DatabaseError.new(err.message, -1)
|
69
|
+
end
|
70
|
+
rescue DBI::DatabaseError => exc
|
71
|
+
exc.set_backtrace(err.backtrace)
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
|
75
|
+
def column_metadata_to_column_info(col)
|
76
|
+
sql_type, type_name, precision, scale =
|
77
|
+
case col.data_type
|
78
|
+
when :char
|
79
|
+
[SQL_CHAR, col.charset_form == :nchar ? "NCHAR" : "CHAR", col.data_size, nil]
|
80
|
+
when :varchar2
|
81
|
+
[SQL_VARCHAR, col.charset_form == :nchar ? "NVARCHAR2" : "VARCHAR2", col.data_size, nil]
|
82
|
+
when :raw
|
83
|
+
[SQL_VARBINARY, "RAW", col.data_size, nil]
|
84
|
+
when :long
|
85
|
+
[SQL_LONGVARCHAR, "LONG", 4000, nil]
|
86
|
+
when :long_raw
|
87
|
+
[SQL_LONGVARBINARY, "LONG RAW", 4000, nil]
|
88
|
+
when :clob
|
89
|
+
[SQL_CLOB, col.charset_form == :nchar ? "NCLOB" : "CLOB", 4000, nil]
|
90
|
+
when :blob
|
91
|
+
[SQL_BLOB, "BLOB", 4000, nil]
|
92
|
+
when :bfile
|
93
|
+
[SQL_BLOB, "BFILE", 4000, nil]
|
94
|
+
when :number
|
95
|
+
if col.scale == -127 && col.precision != 0
|
96
|
+
# To convert from binary to decimal precision, multiply n by 0.30103.
|
97
|
+
[SQL_FLOAT, "FLOAT", (col.precision * 0.30103).ceil , nil]
|
98
|
+
elsif col.precision == 0
|
99
|
+
# NUMBER or calculated value (eg. col * 1.2).
|
100
|
+
[SQL_NUMERIC, "NUMBER", 38, nil]
|
101
|
+
else
|
102
|
+
[SQL_NUMERIC, "NUMBER", col.precision, col.scale]
|
103
|
+
end
|
104
|
+
when :binary_float
|
105
|
+
# (23 * 0.30103).ceil => 7
|
106
|
+
[SQL_FLOAT, "BINARY_FLOAT", 7, nil]
|
107
|
+
when :binary_double
|
108
|
+
# (52 * 0.30103).ceil => 16
|
109
|
+
[SQL_DOUBLE, "BINARY_DOUBLE", 16, nil]
|
110
|
+
when :date
|
111
|
+
# yyyy-mm-dd hh:mi:ss
|
112
|
+
[SQL_DATE, "DATE", 19, nil]
|
113
|
+
when :timestamp
|
114
|
+
# yyyy-mm-dd hh:mi:ss.SSSS
|
115
|
+
[SQL_TIMESTAMP, "TIMESTAMP", 20 + col.fsprecision, nil]
|
116
|
+
when :timestamp_tz
|
117
|
+
# yyyy-mm-dd hh:mi:ss.SSSS +HH:MM
|
118
|
+
[SQL_TIMESTAMP, "TIMESTAMP WITH TIME ZONE", 27 + col.fsprecision, nil]
|
119
|
+
when :timestamp_ltz
|
120
|
+
# yyyy-mm-dd hh:mi:ss.SSSS
|
121
|
+
[SQL_TIMESTAMP, "TIMESTAMP WITH LOCAL TIME ZONE", 20 + col.fsprecision, nil]
|
122
|
+
when :interval_ym
|
123
|
+
# yyyy-mm
|
124
|
+
[SQL_OTHER, 'INTERVAL YEAR TO MONTH', col.lfprecision + 3, nil]
|
125
|
+
when :interval_ds
|
126
|
+
# dd hh:mi:ss.SSSSS
|
127
|
+
[SQL_OTHER, 'INTERVAL DAY TO SECOND', col.lfprecision + 10 + col.fsprecision, nil]
|
128
|
+
else
|
129
|
+
[SQL_OTHER, col.data_type.to_s, nil, nil]
|
130
|
+
end
|
131
|
+
{'name' => col.name,
|
132
|
+
'sql_type' => sql_type,
|
133
|
+
'type_name' => type_name,
|
134
|
+
'nullable' => col.nullable?,
|
135
|
+
'precision' => precision,
|
136
|
+
'scale' => scale,
|
137
|
+
}
|
138
|
+
end
|
139
|
+
private :column_metadata_to_column_info
|
140
|
+
end
|
141
|
+
|
142
|
+
class Driver < DBI::BaseDriver # :nodoc:
|
143
|
+
include Util
|
144
|
+
|
145
|
+
def initialize
|
146
|
+
super(USED_DBD_VERSION)
|
147
|
+
end
|
148
|
+
|
149
|
+
# external OS authentication
|
150
|
+
# (contributed by Dan Fitch)
|
151
|
+
def default_user
|
152
|
+
[nil, nil]
|
153
|
+
end
|
154
|
+
|
155
|
+
def connect( dbname, user, auth, attr )
|
156
|
+
handle = ::OCI8.new(user, auth, dbname, attr['Privilege'])
|
157
|
+
handle.non_blocking = true if attr['NonBlocking']
|
158
|
+
return Database.new(handle, attr)
|
159
|
+
rescue OCIException => err
|
160
|
+
raise_dbierror(err)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Database < DBI::BaseDatabase
|
165
|
+
include Util
|
166
|
+
|
167
|
+
def disconnect
|
168
|
+
@handle.logoff
|
169
|
+
rescue OCIException => err
|
170
|
+
raise_dbierror(err)
|
171
|
+
end
|
172
|
+
|
173
|
+
def prepare( statement )
|
174
|
+
# convert ?-style parameters to :1, :2 etc.
|
175
|
+
prep_statement = DBI::SQL::PreparedStatement.new(DummyQuoter.new, statement)
|
176
|
+
if prep_statement.unbound.size > 0
|
177
|
+
arr = (1..(prep_statement.unbound.size)).collect{|i| ":#{i}"}
|
178
|
+
statement = prep_statement.bind( arr )
|
179
|
+
end
|
180
|
+
cursor = @handle.parse(statement)
|
181
|
+
Statement.new(cursor)
|
182
|
+
rescue OCIException => err
|
183
|
+
raise_dbierror(err)
|
184
|
+
end
|
185
|
+
|
186
|
+
def ping
|
187
|
+
@handle.exec("BEGIN NULL; END;")
|
188
|
+
true
|
189
|
+
rescue
|
190
|
+
false
|
191
|
+
end
|
192
|
+
|
193
|
+
def commit
|
194
|
+
@handle.commit()
|
195
|
+
rescue OCIException => err
|
196
|
+
raise_dbierror(err)
|
197
|
+
end
|
198
|
+
|
199
|
+
def rollback
|
200
|
+
@handle.rollback()
|
201
|
+
rescue OCIException => err
|
202
|
+
raise_dbierror(err)
|
203
|
+
end
|
204
|
+
|
205
|
+
def tables
|
206
|
+
stmt = execute("SELECT object_name FROM user_objects where object_type in ('TABLE', 'VIEW')")
|
207
|
+
rows = stmt.fetch_all || []
|
208
|
+
stmt.finish
|
209
|
+
rows.collect {|row| row[0]}
|
210
|
+
end
|
211
|
+
|
212
|
+
# SQLs are copied from DBD::Oracle.
|
213
|
+
def columns(table)
|
214
|
+
tab = @handle.describe_table(table)
|
215
|
+
cols = tab.columns
|
216
|
+
cols.collect! do |col|
|
217
|
+
column_metadata_to_column_info(col)
|
218
|
+
end
|
219
|
+
|
220
|
+
dbh = DBI::DatabaseHandle.new(self)
|
221
|
+
|
222
|
+
pk_index_name = nil
|
223
|
+
dbh.select_all(<<EOS, tab.obj_schema, tab.obj_name) do |row|
|
224
|
+
select index_name
|
225
|
+
from all_constraints
|
226
|
+
where constraint_type = 'P'
|
227
|
+
and owner = :1
|
228
|
+
and table_name = :2
|
229
|
+
EOS
|
230
|
+
pk_index_name = row[0]
|
231
|
+
end
|
232
|
+
|
233
|
+
indices = {}
|
234
|
+
primaries = {}
|
235
|
+
uniques = {}
|
236
|
+
dbh.select_all(<<EOS, tab.obj_schema, tab.obj_name) do |row|
|
237
|
+
select a.column_name, a.index_name, b.uniqueness
|
238
|
+
from all_ind_columns a, all_indexes b
|
239
|
+
where a.index_name = b.index_name
|
240
|
+
and a.index_owner = b.owner
|
241
|
+
and a.table_owner = :1
|
242
|
+
and a.table_name = :2
|
243
|
+
EOS
|
244
|
+
col_name, index_name, uniqueness = row
|
245
|
+
indices[col_name] = true
|
246
|
+
primaries[col_name] = true if index_name == pk_index_name
|
247
|
+
uniques[col_name] = true if uniqueness == 'UNIQUE'
|
248
|
+
end
|
249
|
+
|
250
|
+
dbh.select_all(<<EOS, tab.obj_schema, tab.obj_name).collect do |row|
|
251
|
+
select column_id, column_name, data_default
|
252
|
+
from all_tab_columns
|
253
|
+
where owner = :1
|
254
|
+
and table_name = :2
|
255
|
+
EOS
|
256
|
+
col_id, col_name, default = row
|
257
|
+
|
258
|
+
col = cols[col_id.to_i - 1]
|
259
|
+
col_name = col['name']
|
260
|
+
|
261
|
+
if default && default[0] == ?'
|
262
|
+
default = default[1..-2].gsub(/''/, "'")
|
263
|
+
end
|
264
|
+
|
265
|
+
col['indexed'] = indices[col_name] || false
|
266
|
+
col['primary'] = primaries[col_name] || false
|
267
|
+
col['unique'] = uniques[col_name] || false
|
268
|
+
col['default'] = default
|
269
|
+
col
|
270
|
+
end
|
271
|
+
rescue OCIException => err
|
272
|
+
raise_dbierror(err)
|
273
|
+
end
|
274
|
+
|
275
|
+
def [](attr)
|
276
|
+
case attr
|
277
|
+
when 'AutoCommit'
|
278
|
+
@handle.autocommit?
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def []=(attr, value)
|
283
|
+
case attr
|
284
|
+
when 'AutoCommit'
|
285
|
+
@handle.autocommit = value
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
private
|
290
|
+
|
291
|
+
class DummyQuoter # :nodoc:
|
292
|
+
# dummy to substitute ?-style parameter markers by :1 :2 etc.
|
293
|
+
def quote(str)
|
294
|
+
str
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class Statement < DBI::BaseStatement
|
300
|
+
include Util
|
301
|
+
|
302
|
+
def initialize(cursor)
|
303
|
+
@cursor = cursor
|
304
|
+
end
|
305
|
+
|
306
|
+
def bind_param( param, value, attribs)
|
307
|
+
if attribs.nil? || attribs['type'].nil?
|
308
|
+
if value.nil?
|
309
|
+
@cursor.bind_param(param, nil, String, 1)
|
310
|
+
else
|
311
|
+
@cursor.bind_param(param, value)
|
312
|
+
end
|
313
|
+
else
|
314
|
+
case attribs['type']
|
315
|
+
when SQL_BINARY
|
316
|
+
type = OCI_TYPECODE_RAW
|
317
|
+
else
|
318
|
+
type = attribs['type']
|
319
|
+
end
|
320
|
+
@cursor.bind_param(param, value, type)
|
321
|
+
end
|
322
|
+
rescue OCIException => err
|
323
|
+
raise_dbierror(err)
|
324
|
+
end
|
325
|
+
|
326
|
+
def execute
|
327
|
+
@cursor.exec
|
328
|
+
rescue OCIException => err
|
329
|
+
raise_dbierror(err)
|
330
|
+
end
|
331
|
+
|
332
|
+
def finish
|
333
|
+
@cursor.close
|
334
|
+
rescue OCIException => err
|
335
|
+
raise_dbierror(err)
|
336
|
+
end
|
337
|
+
|
338
|
+
def fetch
|
339
|
+
@cursor.fetch
|
340
|
+
rescue OCIException => err
|
341
|
+
raise_dbierror(err)
|
342
|
+
end
|
343
|
+
|
344
|
+
def column_info
|
345
|
+
# minimum implementation.
|
346
|
+
@cursor.column_metadata.collect do |md|
|
347
|
+
col = column_metadata_to_column_info(md)
|
348
|
+
col['indexed'] = nil
|
349
|
+
col['primary'] = nil
|
350
|
+
col['unique'] = nil
|
351
|
+
col['default'] = nil
|
352
|
+
col
|
353
|
+
end
|
354
|
+
rescue OCIException => err
|
355
|
+
raise_dbierror(err)
|
356
|
+
end
|
357
|
+
|
358
|
+
def rows
|
359
|
+
@cursor.row_count
|
360
|
+
rescue OCIException => err
|
361
|
+
raise_dbierror(err)
|
362
|
+
end
|
363
|
+
|
364
|
+
def __rowid
|
365
|
+
@cursor.rowid
|
366
|
+
end
|
367
|
+
|
368
|
+
def __define(pos, type, length = nil)
|
369
|
+
@cursor.define(pos, type, length)
|
370
|
+
self
|
371
|
+
end
|
372
|
+
|
373
|
+
def __bind_value(param)
|
374
|
+
@cursor[param]
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
if defined? ::OCI8::BindType::Base
|
379
|
+
##
|
380
|
+
## ruby-oci8 2.0 bind classes.
|
381
|
+
##
|
382
|
+
|
383
|
+
module BindType # :nodoc:
|
384
|
+
|
385
|
+
# helper class to define/bind DBI::Date.
|
386
|
+
class DBIDate < ::OCI8::BindType::OraDate
|
387
|
+
def set(val)
|
388
|
+
# convert val to an OraDate,
|
389
|
+
# then set it to the bind handle.
|
390
|
+
super(val && OraDate.new(val.year, val.month, val.day))
|
391
|
+
end
|
392
|
+
def get()
|
393
|
+
# get an Oradate from the bind handle,
|
394
|
+
# then convert it to a DBI::Date.
|
395
|
+
val = super()
|
396
|
+
return nil if val.nil?
|
397
|
+
DBI::Date.new(val.year, val.month, val.day)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# helper class to define/bind DBI::Timestamp.
|
402
|
+
#
|
403
|
+
# To fetch all Oracle's DATE columns as DBI::Timestamp:
|
404
|
+
# ::OCI8::BindType::Mapping[OCI8::SQLT_DAT] = ::DBI::DBD::OCI8::BindType::DBITimestamp
|
405
|
+
#
|
406
|
+
class DBITimestamp < ::OCI8::BindType::OraDate
|
407
|
+
def set(val)
|
408
|
+
# convert val to an OraDate,
|
409
|
+
# then set it to the bind handle.
|
410
|
+
super(val && OraDate.new(val.year, val.month, val.day,
|
411
|
+
val.respond_to?(:hour) ? val.hour : 0,
|
412
|
+
val.respond_to?(:min) ? val.min : 0,
|
413
|
+
val.respond_to?(:sec) ? val.sec : 0))
|
414
|
+
end
|
415
|
+
def get()
|
416
|
+
# get an Oradate from the bind handle,
|
417
|
+
# then convert it to a DBI::Timestamp.
|
418
|
+
val = super()
|
419
|
+
return nil if val.nil?
|
420
|
+
DBI::Timestamp.new(val.year, val.month, val.day, val.hour, val.minute, val.second)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# helper class to bind ref cursor as DBI::StatementHandle.
|
425
|
+
#
|
426
|
+
# # Create package
|
427
|
+
# dbh.execute(<<EOS)
|
428
|
+
# create or replace package test_pkg is
|
429
|
+
# type ref_cursor is ref cursor;
|
430
|
+
# procedure tab_table(csr out ref_cursor);
|
431
|
+
# end;
|
432
|
+
# EOS
|
433
|
+
#
|
434
|
+
# # Create package body
|
435
|
+
# dbh.execute(<<EOS)
|
436
|
+
# create or replace package body test_pkg is
|
437
|
+
# procedure tab_table(csr out ref_cursor) is
|
438
|
+
# begin
|
439
|
+
# open csr for select * from tab;
|
440
|
+
# end;
|
441
|
+
# end;
|
442
|
+
# EOS
|
443
|
+
#
|
444
|
+
# # Execute test_pkg.tab_table.
|
445
|
+
# # The first parameter is bound as DBI::StatementHandle.
|
446
|
+
# plsql = dbh.execute("begin test_pkg.tab_table(?); end;", DBI::StatementHandle)
|
447
|
+
#
|
448
|
+
# # Get the first parameter, which is a DBI::StatementHandle.
|
449
|
+
# sth = plsql.func(:bind_value, 1)
|
450
|
+
#
|
451
|
+
# # fetch column data.
|
452
|
+
# sth.fetch_all
|
453
|
+
#
|
454
|
+
class DBIStatementHandle < ::OCI8::BindType::Cursor
|
455
|
+
def set(val)
|
456
|
+
if val.is_a? DBI::StatementHandle
|
457
|
+
# get OCI8::Cursor
|
458
|
+
val = val.instance_eval do @handle end
|
459
|
+
val = val.instance_eval do @cursor end
|
460
|
+
end
|
461
|
+
super(val)
|
462
|
+
end
|
463
|
+
def get()
|
464
|
+
val = super
|
465
|
+
return nil if val.nil?
|
466
|
+
stmt = DBI::DBD::OCI8::Statement.new(val)
|
467
|
+
DBI::StatementHandle.new(stmt, true, false)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end # BindType
|
471
|
+
|
472
|
+
else
|
473
|
+
##
|
474
|
+
## ruby-oci8 1.0 bind classes.
|
475
|
+
##
|
476
|
+
|
477
|
+
module BindType # :nodoc:
|
478
|
+
DBIDate = Object.new
|
479
|
+
class << DBIDate
|
480
|
+
def fix_type(env, val, length, precision, scale)
|
481
|
+
# bind as an OraDate
|
482
|
+
[::OCI8::SQLT_DAT, val, nil]
|
483
|
+
end
|
484
|
+
def decorate(b)
|
485
|
+
def b.set(val)
|
486
|
+
# convert val to an OraDate,
|
487
|
+
# then set it to the bind handle.
|
488
|
+
super(val && OraDate.new(val.year, val.month, val.day))
|
489
|
+
end
|
490
|
+
def b.get()
|
491
|
+
# get an Oradate from the bind handle,
|
492
|
+
# then convert it to a DBI::Date.
|
493
|
+
(val = super()) && DBI::Date.new(val.year, val.month, val.day)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
DBITimestamp = Object.new
|
499
|
+
class << DBITimestamp
|
500
|
+
def fix_type(env, val, length, precision, scale)
|
501
|
+
# bind as an OraDate
|
502
|
+
[::OCI8::SQLT_DAT, val, nil]
|
503
|
+
end
|
504
|
+
def decorate(b)
|
505
|
+
def b.set(val)
|
506
|
+
# convert val to an OraDate,
|
507
|
+
# then set it to the bind handle.
|
508
|
+
super(val && OraDate.new(val.year, val.month, val.day,
|
509
|
+
val.respond_to?(:hour) ? val.hour : 0,
|
510
|
+
val.respond_to?(:min) ? val.min : 0,
|
511
|
+
val.respond_to?(:sec) ? val.sec : 0))
|
512
|
+
end
|
513
|
+
def b.get()
|
514
|
+
# get an Oradate from the bind handle,
|
515
|
+
# then convert it to a DBI::Timestamp.
|
516
|
+
(val = super()) && DBI::Timestamp.new(val.year, val.month, val.day, val.hour, val.minute, val.second)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
DBIStatementHandle = Object.new
|
522
|
+
class << DBIStatementHandle
|
523
|
+
def fix_type(env, val, length, precision, scale)
|
524
|
+
raise NotImplementedError unless val.nil?
|
525
|
+
[::OCI8::SQLT_RSET, nil, env.alloc(OCIStmt)]
|
526
|
+
end
|
527
|
+
def decorate(b)
|
528
|
+
def b.set(val)
|
529
|
+
raise NotImplementedError
|
530
|
+
end
|
531
|
+
def b.get()
|
532
|
+
val = super
|
533
|
+
return val if val.nil?
|
534
|
+
cur = ::OCI8::Cursor.new(@env, @svc, @ctx, val)
|
535
|
+
stmt = DBI::DBD::OCI8::Statement.new(cur)
|
536
|
+
DBI::StatementHandle.new(stmt, true, false)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end # BindType
|
541
|
+
end
|
542
|
+
|
543
|
+
::OCI8::BindType::Mapping[DBI::Date] = BindType::DBIDate
|
544
|
+
::OCI8::BindType::Mapping[DBI::Timestamp] = BindType::DBITimestamp
|
545
|
+
::OCI8::BindType::Mapping[DBI::StatementHandle] = BindType::DBIStatementHandle
|
546
|
+
|
547
|
+
end # module OCI8
|
548
|
+
end # module DBD
|
549
|
+
end # module DBI
|