ruby-oci8 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/ChangeLog +569 -0
  2. data/Makefile +51 -0
  3. data/NEWS +322 -0
  4. data/README +415 -0
  5. data/VERSION +1 -0
  6. data/dist-files +70 -0
  7. data/doc/api.en.html +527 -0
  8. data/doc/api.en.rd +554 -0
  9. data/doc/api.ja.html +525 -0
  10. data/doc/api.ja.rd +557 -0
  11. data/doc/manual.css +35 -0
  12. data/ext/oci8/MANIFEST +22 -0
  13. data/ext/oci8/attr.c +415 -0
  14. data/ext/oci8/bind.c +194 -0
  15. data/ext/oci8/const.c +165 -0
  16. data/ext/oci8/define.c +53 -0
  17. data/ext/oci8/describe.c +81 -0
  18. data/ext/oci8/descriptor.c +39 -0
  19. data/ext/oci8/env.c +276 -0
  20. data/ext/oci8/error.c +234 -0
  21. data/ext/oci8/extconf.rb +118 -0
  22. data/ext/oci8/handle.c +262 -0
  23. data/ext/oci8/lob.c +386 -0
  24. data/ext/oci8/oci8.c +137 -0
  25. data/ext/oci8/oci8.h +345 -0
  26. data/ext/oci8/ocinumber.c +117 -0
  27. data/ext/oci8/oraconf.rb +1026 -0
  28. data/ext/oci8/oradate.c +426 -0
  29. data/ext/oci8/oranumber.c +445 -0
  30. data/ext/oci8/param.c +37 -0
  31. data/ext/oci8/post-config.rb +5 -0
  32. data/ext/oci8/server.c +182 -0
  33. data/ext/oci8/session.c +99 -0
  34. data/ext/oci8/stmt.c +624 -0
  35. data/ext/oci8/svcctx.c +229 -0
  36. data/lib/DBD/OCI8/OCI8.rb +549 -0
  37. data/lib/oci8.rb.in +1605 -0
  38. data/metaconfig +142 -0
  39. data/pre-distclean.rb +7 -0
  40. data/ruby-oci8.gemspec +54 -0
  41. data/ruby-oci8.spec +62 -0
  42. data/setup.rb +1331 -0
  43. data/support/README +4 -0
  44. data/support/runit/assert.rb +281 -0
  45. data/support/runit/cui/testrunner.rb +101 -0
  46. data/support/runit/error.rb +4 -0
  47. data/support/runit/method_mappable.rb +20 -0
  48. data/support/runit/robserver.rb +25 -0
  49. data/support/runit/setuppable.rb +15 -0
  50. data/support/runit/teardownable.rb +16 -0
  51. data/support/runit/testcase.rb +113 -0
  52. data/support/runit/testfailure.rb +25 -0
  53. data/support/runit/testresult.rb +121 -0
  54. data/support/runit/testsuite.rb +43 -0
  55. data/support/runit/version.rb +3 -0
  56. data/test/README +4 -0
  57. data/test/config.rb +129 -0
  58. data/test/test_all.rb +43 -0
  59. data/test/test_bind_raw.rb +53 -0
  60. data/test/test_bind_time.rb +191 -0
  61. data/test/test_break.rb +81 -0
  62. data/test/test_clob.rb +101 -0
  63. data/test/test_connstr.rb +80 -0
  64. data/test/test_dbi.rb +317 -0
  65. data/test/test_dbi_clob.rb +56 -0
  66. data/test/test_describe.rb +137 -0
  67. data/test/test_metadata.rb +243 -0
  68. data/test/test_oci8.rb +273 -0
  69. data/test/test_oradate.rb +263 -0
  70. data/test/test_oranumber.rb +149 -0
  71. metadata +118 -0
@@ -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