ruby-oci8 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|