ruby-oci8 1.0.6-x86-mswin32-60

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.
Files changed (50) hide show
  1. data/ChangeLog +693 -0
  2. data/Makefile +51 -0
  3. data/NEWS +407 -0
  4. data/README +415 -0
  5. data/VERSION +1 -0
  6. data/dist-files +71 -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/oci8lib.so +0 -0
  13. data/lib/dbd/OCI8.rb +591 -0
  14. data/lib/oci8.rb +1655 -0
  15. data/lib/oci8.rb.in +1655 -0
  16. data/metaconfig +142 -0
  17. data/pre-distclean.rb +7 -0
  18. data/ruby-oci8.gemspec +54 -0
  19. data/ruby-oci8.spec +62 -0
  20. data/setup.rb +1331 -0
  21. data/support/README +4 -0
  22. data/support/runit/assert.rb +281 -0
  23. data/support/runit/cui/testrunner.rb +101 -0
  24. data/support/runit/error.rb +4 -0
  25. data/support/runit/method_mappable.rb +20 -0
  26. data/support/runit/robserver.rb +25 -0
  27. data/support/runit/setuppable.rb +15 -0
  28. data/support/runit/teardownable.rb +16 -0
  29. data/support/runit/testcase.rb +113 -0
  30. data/support/runit/testfailure.rb +25 -0
  31. data/support/runit/testresult.rb +121 -0
  32. data/support/runit/testsuite.rb +43 -0
  33. data/support/runit/version.rb +3 -0
  34. data/test/README +4 -0
  35. data/test/config.rb +129 -0
  36. data/test/test_all.rb +48 -0
  37. data/test/test_bind_raw.rb +53 -0
  38. data/test/test_bind_time.rb +191 -0
  39. data/test/test_break.rb +81 -0
  40. data/test/test_clob.rb +101 -0
  41. data/test/test_connstr.rb +80 -0
  42. data/test/test_dbi.rb +327 -0
  43. data/test/test_dbi_clob.rb +58 -0
  44. data/test/test_describe.rb +137 -0
  45. data/test/test_metadata.rb +243 -0
  46. data/test/test_oci8.rb +273 -0
  47. data/test/test_oradate.rb +263 -0
  48. data/test/test_oranumber.rb +149 -0
  49. data/test/test_rowid.rb +38 -0
  50. metadata +105 -0
@@ -0,0 +1,1655 @@
1
+ # --*- ruby -*--
2
+ # This is based on yoshidam's oracle.rb.
3
+ #
4
+ # sample one liner:
5
+ # ruby -r oci8 -e 'OCI8.new("scott", "tiger", nil).exec("select * from emp") do |r| puts r.join(","); end'
6
+ # # select all data from emp and print them as CVS format.
7
+
8
+ if RUBY_PLATFORM =~ /cygwin/
9
+ # Cygwin manages environment variables by itself.
10
+ # They don't synchroize with Win32's ones.
11
+ # This set some Oracle's environment variables to win32's enviroment.
12
+ require 'Win32API'
13
+ win32setenv = Win32API.new('Kernel32.dll', 'SetEnvironmentVariableA', 'PP', 'I')
14
+ ['NLS_LANG', 'ORA_NLS10', 'ORA_NLS32', 'ORA_NLS33', 'ORACLE_BASE', 'ORACLE_HOME', 'ORACLE_SID', 'TNS_ADMIN', 'LOCAL'].each do |name|
15
+ val = ENV[name]
16
+ win32setenv.call(name, val && val.dup)
17
+ end
18
+ end
19
+
20
+ require 'oci8lib'
21
+ require 'date'
22
+ require 'thread'
23
+
24
+ class OCIBreak < OCIException
25
+ def initialize(errstr = "Canceled by user request.")
26
+ super(errstr)
27
+ end
28
+ end
29
+
30
+ class OCIDefine # :nodoc:
31
+ # define handle of OCILobLocator needs @env and @svc.
32
+ def set_handle(env, svc, ctx)
33
+ @env = env
34
+ @svc = svc
35
+ @ctx = ctx
36
+ end
37
+ end
38
+
39
+ class OCIBind # :nodoc:
40
+ # define handle of OCILobLocator needs @env and @svc.
41
+ def set_handle(env, svc, ctx)
42
+ @env = env
43
+ @svc = svc
44
+ @ctx = ctx
45
+ end
46
+ end
47
+
48
+ class OCI8
49
+ @@error_in_initialization = nil
50
+ begin
51
+ OCIEnv.initialise(OCI_OBJECT)
52
+ @@env = OCIEnv.init()
53
+ rescue OCIError
54
+ # don't raise this error at this time.
55
+ @@error_in_initialization = $!
56
+ end
57
+
58
+ VERSION = '@@OCI8_MODULE_VERSION@@'
59
+ CLIENT_VERSION = '@@OCI8_CLIENT_VERSION@@'
60
+ # :stopdoc:
61
+ RAW = OCI_TYPECODE_RAW
62
+ STMT_SELECT = OCI_STMT_SELECT
63
+ STMT_UPDATE = OCI_STMT_UPDATE
64
+ STMT_DELETE = OCI_STMT_DELETE
65
+ STMT_INSERT = OCI_STMT_INSERT
66
+ STMT_CREATE = OCI_STMT_CREATE
67
+ STMT_DROP = OCI_STMT_DROP
68
+ STMT_ALTER = OCI_STMT_ALTER
69
+ STMT_BEGIN = OCI_STMT_BEGIN
70
+ STMT_DECLARE = OCI_STMT_DECLARE
71
+ # :startdoc:
72
+
73
+ # sql type (varchar, varchar2)
74
+ SQLT_CHR = 1
75
+ # sql type (number, double precision, float, real, numeric, int, integer, smallint)
76
+ SQLT_NUM = 2
77
+ # sql type (long)
78
+ SQLT_LNG = 8
79
+ # sql type (date)
80
+ SQLT_DAT = 12
81
+ # sql type (raw)
82
+ SQLT_BIN = 23
83
+ # sql type (long raw)
84
+ SQLT_LBI = 24
85
+ # sql type (char)
86
+ SQLT_AFC = 96
87
+ # sql type (binary_float)
88
+ SQLT_IBFLOAT = 100
89
+ # sql type (binary_double)
90
+ SQLT_IBDOUBLE = 101
91
+ # sql type (rowid)
92
+ SQLT_RDD = 104
93
+ # sql type (clob)
94
+ SQLT_CLOB = 112
95
+ # sql type (blob)
96
+ SQLT_BLOB = 113
97
+ # sql type (bfile)
98
+ SQLT_BFILE = 114
99
+ # sql type (result set)
100
+ SQLT_RSET = 116
101
+ # sql type (timestamp), not supported yet.
102
+ #
103
+ # If you want to fetch a timestamp before native timestamp data type
104
+ # will be supported, fetch data as an OraDate by adding the following
105
+ # code to your code.
106
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::OraDate
107
+ SQLT_TIMESTAMP = 187
108
+ # sql type (timestamp with time zone), not supported yet
109
+ SQLT_TIMESTAMP_TZ = 188
110
+ # sql type (interval year to month), not supported yet
111
+ SQLT_INTERVAL_YM = 189
112
+ # sql type (interval day to second), not supported yet
113
+ SQLT_INTERVAL_DS = 190
114
+ # sql type (timestamp with local time zone), not supported yet
115
+ SQLT_TIMESTAMP_LTZ = 232
116
+
117
+ # charset form
118
+ SQLCS_IMPLICIT = 1
119
+ SQLCS_NCHAR = 2
120
+
121
+ # mapping of sql type number to sql type name.
122
+ SQLT_NAMES = {}
123
+ constants.each do |name|
124
+ next if name.index("SQLT_") != 0
125
+ val = const_get name.intern
126
+ if val.is_a? Fixnum
127
+ SQLT_NAMES[val] = name
128
+ end
129
+ end
130
+
131
+ module Util # :nodoc:
132
+ CTX_EXECFLAG = 0
133
+ CTX_MUTEX = 1
134
+ CTX_THREAD = 2
135
+ CTX_LONG_READ_LEN = 3
136
+
137
+ def do_ocicall(ctx)
138
+ sleep_time = 0.01
139
+ ctx[CTX_MUTEX].lock
140
+ ctx[CTX_THREAD] = Thread.current
141
+ begin
142
+ yield
143
+ rescue OCIStillExecuting # non-blocking mode
144
+ ctx[CTX_MUTEX].unlock
145
+ sleep(sleep_time)
146
+ ctx[CTX_MUTEX].lock
147
+ if ctx[CTX_THREAD].nil?
148
+ raise OCIBreak
149
+ end
150
+ # expand sleep time to prevent busy loop.
151
+ sleep_time *= 2 if sleep_time < 0.5
152
+ retry
153
+ ensure
154
+ ctx[CTX_THREAD] = nil
155
+ ctx[CTX_MUTEX].unlock
156
+ end
157
+ end # do_ocicall
158
+ end
159
+ include Util
160
+
161
+ def parse_connect_string(connstr)
162
+ if connstr !~ /^([^(\s|\@)]*)\/([^(\s|\@)]*)(?:\@(\S+))?(?:\s+as\s+(\S*)\s*)?$/i
163
+ raise ArgumentError, %Q{invalid connect string "#{connstr}" (expect "username/password[@(tns_name|//host[:port]/service_name)][ as (sysdba|sysoper)]")}
164
+ end
165
+ uid, pswd, conn, privilege = $1, $2, $3, $4
166
+ case privilege.upcase
167
+ when 'SYSDBA'
168
+ privilege = :SYSDBA
169
+ when 'SYSOPER'
170
+ privilege = :SYSOPER
171
+ end if privilege
172
+ if uid.length == 0 && pswd.length == 0
173
+ # external credential
174
+ uid = nil
175
+ pswd = nil
176
+ end
177
+ return uid, pswd, conn, privilege
178
+ end
179
+ private :parse_connect_string
180
+
181
+ def initialize(*args)
182
+ raise @@error_in_initialization if @@error_in_initialization
183
+ case args.length
184
+ when 1
185
+ uid, pswd, conn, privilege = parse_connect_string(args[0])
186
+ when 2, 3, 4
187
+ uid, pswd, conn, privilege = *args
188
+ else
189
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1..4)"
190
+ end
191
+ case privilege
192
+ when nil
193
+ @privilege = nil
194
+ when :SYSDBA
195
+ @privilege = OCI_SYSDBA
196
+ when :SYSOPER
197
+ @privilege = OCI_SYSOPER
198
+ else
199
+ raise ArgumentError, "invalid privilege name #{privilege} (expect :SYSDBA, :SYSOPER or nil)"
200
+ end
201
+
202
+ @prefetch_rows = nil
203
+ @ctx = [0, Mutex.new, nil, 65535]
204
+ if @privilege or (uid.nil? and pswd.nil?)
205
+ @svc = @@env.alloc(OCISvcCtx)
206
+ @srv = @@env.alloc(OCIServer)
207
+ @auth = @@env.alloc(OCISession)
208
+ @privilege ||= OCI_DEFAULT
209
+
210
+ if uid.nil? and pswd.nil?
211
+ # external credential
212
+ cred = OCI_CRED_EXT
213
+ else
214
+ # RDBMS credential
215
+ cred = OCI_CRED_RDBMS
216
+ @auth.attrSet(OCI_ATTR_USERNAME, uid)
217
+ @auth.attrSet(OCI_ATTR_PASSWORD, pswd)
218
+ end
219
+ do_ocicall(@ctx) { @srv.attach(conn) }
220
+ begin
221
+ @svc.attrSet(OCI_ATTR_SERVER, @srv)
222
+ do_ocicall(@ctx) { @auth.begin(@svc, cred, @privilege) }
223
+ @svc.attrSet(OCI_ATTR_SESSION, @auth)
224
+ rescue
225
+ @srv.detach()
226
+ raise
227
+ end
228
+ else
229
+ @svc = @@env.logon(uid, pswd, conn)
230
+ end
231
+ @svc.instance_variable_set(:@env, @@env)
232
+ end # initialize
233
+
234
+ def logoff
235
+ rollback()
236
+ if @privilege
237
+ do_ocicall(@ctx) { @auth.end(@svc) }
238
+ do_ocicall(@ctx) { @srv.detach() }
239
+ else
240
+ @svc.logoff
241
+ end
242
+ @svc.free()
243
+ true
244
+ end # logoff
245
+
246
+ def exec(sql, *bindvars)
247
+ cursor = OCI8::Cursor.new(@@env, @svc, @ctx)
248
+ cursor.prefetch_rows = @prefetch_rows if @prefetch_rows
249
+ cursor.parse(sql)
250
+ if cursor.type == OCI_STMT_SELECT && ! block_given?
251
+ cursor.exec(*bindvars)
252
+ cursor
253
+ else
254
+ begin
255
+ ret = cursor.exec(*bindvars)
256
+ case cursor.type
257
+ when OCI_STMT_SELECT
258
+ cursor.fetch { |row| yield(row) } # for each row
259
+ cursor.row_count()
260
+ when OCI_STMT_BEGIN, OCI_STMT_DECLARE # PL/SQL block
261
+ ary = []
262
+ cursor.keys.sort.each do |key|
263
+ ary << cursor[key]
264
+ end
265
+ ary
266
+ else
267
+ ret
268
+ end
269
+ ensure
270
+ cursor.close
271
+ end
272
+ end
273
+ end # exec
274
+
275
+ def parse(sql)
276
+ cursor = OCI8::Cursor.new(@@env, @svc, @ctx)
277
+ cursor.prefetch_rows = @prefetch_rows if @prefetch_rows
278
+ cursor.parse(sql)
279
+ cursor
280
+ end # parse
281
+
282
+ def commit
283
+ do_ocicall(@ctx) { @svc.commit }
284
+ self
285
+ end # commit
286
+
287
+ def rollback
288
+ do_ocicall(@ctx) { @svc.rollback }
289
+ self
290
+ end # rollback
291
+
292
+ def autocommit?
293
+ (@ctx[CTX_EXECFLAG] & OCI_COMMIT_ON_SUCCESS) == OCI_COMMIT_ON_SUCCESS
294
+ end # autocommit?
295
+
296
+ # add alias compatible with 'Oracle7 Module for Ruby'.
297
+ alias autocommit autocommit?
298
+
299
+ def autocommit=(ac)
300
+ if ac
301
+ commit()
302
+ @ctx[CTX_EXECFLAG] |= OCI_COMMIT_ON_SUCCESS
303
+ else
304
+ @ctx[CTX_EXECFLAG] &= ~OCI_COMMIT_ON_SUCCESS
305
+ end
306
+ ac
307
+ end # autocommit=
308
+
309
+ def prefetch_rows=(rows)
310
+ @prefetch_rows = rows
311
+ end
312
+
313
+ def non_blocking?
314
+ @svc.attrGet(OCI_ATTR_NONBLOCKING_MODE)
315
+ end # non_blocking?
316
+
317
+ def non_blocking=(nb)
318
+ if (nb ? true : false) != non_blocking?
319
+ # If the argument and the current status are different,
320
+ # toggle blocking / non-blocking.
321
+ @srv = @svc.attrGet(OCI_ATTR_SERVER) unless @srv
322
+ @srv.attrSet(OCI_ATTR_NONBLOCKING_MODE, nil)
323
+ end
324
+ end # non_blocking=
325
+
326
+ def break
327
+ @ctx[CTX_MUTEX].synchronize do
328
+ @svc.break()
329
+ unless @ctx[CTX_THREAD].nil?
330
+ @ctx[CTX_THREAD].wakeup()
331
+ @ctx[CTX_THREAD] = nil
332
+ if @svc.respond_to?("reset")
333
+ begin
334
+ @svc.reset()
335
+ rescue OCIError
336
+ raise if $!.code != 1013 # ORA-01013
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end # break
342
+
343
+ def long_read_len
344
+ @ctx[OCI8::Util::CTX_LONG_READ_LEN]
345
+ end
346
+
347
+ def long_read_len=(len)
348
+ @ctx[OCI8::Util::CTX_LONG_READ_LEN] = len
349
+ end
350
+
351
+ def describe_table(table_name)
352
+ desc = @@env.alloc(OCIDescribe)
353
+ desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1)
354
+ do_ocicall(@ctx) { desc.describeAny(@svc, table_name.to_s, OCI_PTYPE_UNK) }
355
+ param = desc.attrGet(OCI_ATTR_PARAM)
356
+
357
+ case param.attrGet(OCI_ATTR_PTYPE)
358
+ when OCI_PTYPE_TABLE
359
+ OCI8::Metadata::Table.new(param)
360
+ when OCI_PTYPE_VIEW
361
+ OCI8::Metadata::View.new(param)
362
+ when OCI_PTYPE_SYN
363
+ schema_name = param.attrGet(OCI_ATTR_SCHEMA_NAME)
364
+ name = param.attrGet(OCI_ATTR_NAME)
365
+ link = param.attrGet(OCI_ATTR_LINK)
366
+ if link.length != 0
367
+ translated_name = schema_name + '.' + name + '@' + link
368
+ else
369
+ translated_name = schema_name + '.' + name
370
+ end
371
+ describe_table(translated_name)
372
+ else
373
+ raise OCIError.new("ORA-04043: object #{table_name} does not exist", 4043)
374
+ end
375
+ end
376
+
377
+ module BindType
378
+ # get/set String
379
+ String = Object.new
380
+ class << String
381
+ def fix_type(env, val, length, precision, scale)
382
+ [OCI8::SQLT_CHR, val, length || (val.nil? ? nil : val.length)]
383
+ end
384
+ end
385
+
386
+ # get/set RAW
387
+ RAW = Object.new
388
+ class << RAW
389
+ def fix_type(env, val, length, precision, scale)
390
+ [OCI8::SQLT_BIN, val, length || (val.nil? ? nil : val.length)]
391
+ end
392
+ end
393
+
394
+ # get/set OraDate
395
+ OraDate = Object.new
396
+ class << OraDate
397
+ def fix_type(env, val, length, precision, scale)
398
+ [OCI8::SQLT_DAT, val, nil]
399
+ end
400
+ end
401
+
402
+ # get/set Time
403
+ Time = Object.new
404
+ class << Time
405
+ def fix_type(env, val, length, precision, scale)
406
+ [OCI8::SQLT_DAT, val, nil]
407
+ end
408
+ def decorate(b)
409
+ def b.set(val)
410
+ super(val && ::OraDate.new(val.year, val.mon, val.mday, val.hour, val.min, val.sec))
411
+ end
412
+ def b.get()
413
+ (val = super()) && val.to_time
414
+ end
415
+ end
416
+ end
417
+
418
+ # get/set Date
419
+ Date = Object.new
420
+ class << Date
421
+ def fix_type(env, val, length, precision, scale)
422
+ [OCI8::SQLT_DAT, val, nil]
423
+ end
424
+ def decorate(b)
425
+ def b.set(val)
426
+ super(val && ::OraDate.new(val.year, val.mon, val.mday))
427
+ end
428
+ def b.get()
429
+ (val = super()) && val.to_date
430
+ end
431
+ end
432
+ end
433
+
434
+ if defined? ::DateTime # ruby 1.8.0 or upper
435
+ # get/set DateTime
436
+ DateTime = Object.new
437
+ class << DateTime
438
+ def fix_type(env, val, length, precision, scale)
439
+ [OCI8::SQLT_DAT, val, nil]
440
+ end
441
+ def decorate(b)
442
+ def b.set(val)
443
+ super(val && ::OraDate.new(val.year, val.mon, val.mday, val.hour, val.min, val.sec))
444
+ end
445
+ def b.get()
446
+ (val = super()) && val.to_datetime
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ # get/set Float
453
+ Float = Object.new
454
+ class << Float
455
+ def fix_type(env, val, length, precision, scale)
456
+ [::Float, val, nil]
457
+ end
458
+ end
459
+
460
+ if defined? OCI_TYPECODE_BDOUBLE
461
+ BinaryDouble = Object.new
462
+ class << BinaryDouble
463
+ def fix_type(env, val, length, precision, scale)
464
+ [SQLT_IBDOUBLE, val, nil]
465
+ end
466
+ end
467
+ end
468
+
469
+ # get/set Fixnum
470
+ Fixnum = Object.new
471
+ class << Fixnum
472
+ def fix_type(env, val, length, precision, scale)
473
+ [::Fixnum, val, nil]
474
+ end
475
+ end
476
+
477
+ # get/set Integer
478
+ Integer = Object.new
479
+ class << Integer
480
+ def fix_type(env, val, length, precision, scale)
481
+ [::Integer, val, nil]
482
+ end
483
+ end
484
+
485
+ # get/set OraNumber
486
+ OraNumber = Object.new
487
+ class << OraNumber
488
+ def fix_type(env, val, length, precision, scale)
489
+ [::OraNumber, val, nil]
490
+ end
491
+ end
492
+
493
+ # get/set Number (for OCI8::SQLT_NUM)
494
+ Number = Object.new
495
+ class << Number
496
+ def fix_type(env, val, length, precision, scale)
497
+ if scale == -127
498
+ if precision == 0
499
+ # NUMBER declared without its scale and precision. (Oracle 9.2.0.3 or above)
500
+ ::OCI8::BindType::Mapping[:number_no_prec_setting].fix_type(env, val, length, precision, scale)
501
+ else
502
+ # FLOAT or FLOAT(p)
503
+ [::Float, val, nil]
504
+ end
505
+ elsif scale == 0
506
+ if precision == 0
507
+ # NUMBER whose scale and precision is unknown
508
+ # or
509
+ # NUMBER declared without its scale and precision. (Oracle 9.2.0.2 or below)
510
+ ::OCI8::BindType::Mapping[:number_unknown_prec].fix_type(env, val, length, precision, scale)
511
+ elsif precision <= 9
512
+ # NUMBER(p, 0); p is less than or equals to the precision of Fixnum
513
+ [::Fixnum, val, nil]
514
+ else
515
+ # NUMBER(p, 0); p is greater than the precision of Fixnum
516
+ [::Integer, val, nil]
517
+ end
518
+ else
519
+ # NUMBER(p, s)
520
+ if precision < 15 # the precision of double.
521
+ [::Float, val, nil]
522
+ else
523
+ # use BigDecimal instead?
524
+ [::OraNumber, val, nil]
525
+ end
526
+ end
527
+ end
528
+ end
529
+
530
+ # get/set OCIRowid
531
+ OCIRowid = Object.new
532
+ class << OCIRowid
533
+ def fix_type(env, val, length, precision, scale)
534
+ [OCI8::SQLT_RDD, nil, val]
535
+ end
536
+ end
537
+
538
+ # get/set BLOB
539
+ BLOB = Object.new
540
+ class << BLOB
541
+ def check_type(val)
542
+ raise ArgumentError, "invalid argument: #{val.class} (expect OCI8::BLOB)" unless val.is_a? OCI8::BLOB
543
+ end
544
+ def fix_type(env, val, length, precision, scale)
545
+ unless val.nil?
546
+ check_type(val)
547
+ val = val.instance_variable_get(:@locator)
548
+ end
549
+ [OCI8::SQLT_BLOB, nil, val]
550
+ end
551
+ def decorate(b)
552
+ def b.set(val)
553
+ check_type(val)
554
+ val = val.instance_variable_get(:@locator)
555
+ super(val)
556
+ end
557
+ def b.get()
558
+ (val = super()) && OCI8::BLOB.new(@svc, val.clone(@svc))
559
+ end
560
+ end
561
+ end
562
+
563
+ # get/set CLOB
564
+ CLOB = Object.new
565
+ class << CLOB
566
+ def check_type(val)
567
+ raise ArgumentError, "invalid argument: #{val.class} (expect OCI8::CLOB)" unless val.is_a? OCI8::CLOB
568
+ end
569
+ def fix_type(env, val, length, precision, scale)
570
+ unless val.nil?
571
+ check_type(val)
572
+ val = val.instance_variable_get(:@locator)
573
+ end
574
+ [OCI8::SQLT_CLOB, nil, val]
575
+ end
576
+ def decorate(b)
577
+ def b.set(val)
578
+ check_type(val)
579
+ val = val.instance_variable_get(:@locator)
580
+ super(val)
581
+ end
582
+ def b.get()
583
+ (val = super()) && OCI8::CLOB.new(@svc, val.clone(@svc))
584
+ end
585
+ end
586
+ end
587
+
588
+ # get/set NCLOB
589
+ NCLOB = Object.new
590
+ class << NCLOB
591
+ def check_type(val)
592
+ raise ArgumentError, "invalid argument: #{val.class} (expect OCI8::NCLOB)" unless val.is_a? OCI8::NCLOB
593
+ end
594
+ def fix_type(env, val, length, precision, scale)
595
+ unless val.nil?
596
+ check_type(val)
597
+ val = val.instance_variable_get(:@locator)
598
+ end
599
+ [OCI8::SQLT_CLOB, nil, val]
600
+ end
601
+ def decorate(b)
602
+ b.attrSet(OCI_ATTR_CHARSET_FORM, SQLCS_NCHAR)
603
+ def b.set(val)
604
+ check_type(val)
605
+ val = val.instance_variable_get(:@locator)
606
+ super(val)
607
+ end
608
+ def b.get()
609
+ (val = super()) && OCI8::NCLOB.new(@svc, val.clone(@svc))
610
+ end
611
+ end
612
+ end
613
+
614
+ # get/set BFILE
615
+ BFILE = Object.new
616
+ class << BFILE
617
+ def check_type(val)
618
+ raise ArgumentError, "invalid argument: #{val.class} (expect OCI8::BFILE)" unless val.is_a? OCI8::BFILE
619
+ end
620
+ def fix_type(env, val, length, precision, scale)
621
+ unless val.nil?
622
+ check_type(val)
623
+ val = val.instance_variable_get(:@locator)
624
+ end
625
+ [OCI8::SQLT_BFILE, nil, val]
626
+ end
627
+ def decorate(b)
628
+ def b.set(val)
629
+ check_type(val)
630
+ val = val.instance_variable_get(:@locator)
631
+ super(val)
632
+ end
633
+ def b.get()
634
+ (val = super()) && OCI8::BFILE.new(@svc, val.clone(@svc))
635
+ end
636
+ end
637
+ end
638
+
639
+ # get Cursor
640
+ Cursor = Object.new
641
+ class << Cursor
642
+ def fix_type(env, val, length, precision, scale)
643
+ [OCI8::SQLT_RSET, nil, val]
644
+ end
645
+ def decorate(b)
646
+ def b.get()
647
+ (val = super()) && OCI8::Cursor.new(@env, @svc, @ctx, val)
648
+ end
649
+ def b.pre_fetch_hook()
650
+ set(@env.alloc(OCIStmt))
651
+ end
652
+ end
653
+ end
654
+
655
+ Mapping = {}
656
+ end # BindType
657
+
658
+ class Cursor
659
+
660
+ include OCI8::Util
661
+
662
+ # for backward compatibility
663
+ def self.select_number_as=(val) # :nodoc:
664
+ if val == ::Fixnum
665
+ bind_type = ::OCI8::BindType::Fixnum
666
+ elsif val == ::Integer
667
+ bind_type = ::OCI8::BindType::Integer
668
+ elsif val == ::Float
669
+ bind_type = ::OCI8::BindType::Float
670
+ else
671
+ raise ArgumentError, "must be Fixnum, Integer or Float"
672
+ end
673
+ ::OCI8::BindType::Mapping[:number_unknown_prec] = bind_type
674
+ end
675
+
676
+ # for backward compatibility
677
+ def self.select_number_as # :nodoc:
678
+ ::OCI8::BindType::Mapping[:number_unknown_prec].fix_type(nil, nil, nil, nil, nil)[0]
679
+ end
680
+
681
+ def initialize(env, svc, ctx, stmt = nil)
682
+ if Process.pid != svc.pid
683
+ raise "The connection cannot be reused in the forked process."
684
+ end
685
+ @env = env
686
+ @svc = svc
687
+ @ctx = ctx
688
+ @binds = nil
689
+ @parms = []
690
+ @defns = nil
691
+ if stmt.nil?
692
+ @stmt = @env.alloc(OCIStmt)
693
+ @stmttype = nil
694
+ else
695
+ @stmt = stmt
696
+ @stmttype = @stmt.attrGet(OCI_ATTR_STMT_TYPE)
697
+ define_columns()
698
+ end
699
+ end # initialize
700
+
701
+ def parse(sql)
702
+ free_binds()
703
+ @parms = []
704
+ @stmt.prepare(sql)
705
+ @stmttype = do_ocicall(@ctx) { @stmt.attrGet(OCI_ATTR_STMT_TYPE) }
706
+ end # parse
707
+
708
+ def define(pos, type, length = nil)
709
+ @defns = [] if @defns.nil?
710
+ if type == String and length.nil?
711
+ length = 4000
712
+ end
713
+ b = bind_or_define(:define, pos, nil, type, length, nil, nil, false)
714
+ @defns[pos].free() unless @defns[pos].nil?
715
+ @defns[pos] = b
716
+ self
717
+ end # define
718
+
719
+ def bind_param(key, val, type = nil, length = nil)
720
+ @binds = {} if @binds.nil?
721
+ b = bind_or_define(:bind, key, val, type, length, nil, nil, false)
722
+ @binds[key].free() unless @binds[key].nil?
723
+ @binds[key] = b
724
+ self
725
+ end # bind_param
726
+
727
+ # get bind value
728
+ def [](key)
729
+ if @binds.nil? or @binds[key].nil?
730
+ return nil
731
+ end
732
+ @binds[key].get()
733
+ end
734
+
735
+ # set bind value
736
+ def []=(key, val)
737
+ if @binds.nil? or @binds[key].nil?
738
+ return nil
739
+ end
740
+ @binds[key].set(val)
741
+ end
742
+
743
+ # get bind keys
744
+ def keys
745
+ if @binds.nil?
746
+ []
747
+ else
748
+ @binds.keys
749
+ end
750
+ end
751
+
752
+ def exec(*bindvars)
753
+ bind_params(*bindvars)
754
+ case @stmttype
755
+ when OCI_STMT_SELECT
756
+ do_ocicall(@ctx) { @stmt.execute(@svc, 0, OCI_DEFAULT) }
757
+ define_columns()
758
+ else
759
+ do_ocicall(@ctx) { @stmt.execute(@svc, 1, @ctx[CTX_EXECFLAG]) }
760
+ @stmt.attrGet(OCI_ATTR_ROW_COUNT)
761
+ end
762
+ end # exec
763
+
764
+ def type
765
+ @stmttype
766
+ end
767
+
768
+ def row_count
769
+ @stmt.attrGet(OCI_ATTR_ROW_COUNT)
770
+ end
771
+
772
+ def get_col_names
773
+ @parms.collect do |p|
774
+ do_ocicall(@ctx) { p.attrGet(OCI_ATTR_NAME) }
775
+ end
776
+ end # get_col_names
777
+
778
+ # add alias compatible with 'Oracle7 Module for Ruby'.
779
+ alias getColNames get_col_names
780
+
781
+ def column_metadata
782
+ @parms.collect do |p|
783
+ OCI8::Metadata::Column.new(p)
784
+ end
785
+ end
786
+
787
+ def fetch
788
+ if iterator?
789
+ while ret = fetch_a_row()
790
+ yield(ret)
791
+ end
792
+ else
793
+ fetch_a_row()
794
+ end
795
+ end # fetch
796
+
797
+ def fetch_hash
798
+ if iterator?
799
+ while ret = fetch_a_hash_row()
800
+ yield(ret)
801
+ end
802
+ else
803
+ fetch_a_hash_row
804
+ end
805
+ end # fetch_hash
806
+
807
+ def close
808
+ @env = nil
809
+ @svc = nil
810
+ free_defns()
811
+ free_binds()
812
+ @stmt.free()
813
+ @parms = nil
814
+ @stmttype = nil
815
+ end # close
816
+
817
+ # Get the rowid of the last inserted/updated/deleted row.
818
+ def rowid
819
+ # get the binary rowid
820
+ rid = @stmt.attrGet(OCI_ATTR_ROWID)
821
+ # convert it to a string rowid.
822
+ if rid.respond_to? :to_s
823
+ # (Oracle 9.0 or upper)
824
+ rid.to_s
825
+ else
826
+ # (Oracle 8.1 or lower)
827
+ stmt = @env.alloc(OCIStmt)
828
+ stmt.prepare('begin :1 := :2; end;')
829
+ b = stmt.bindByPos(1, OCI8::SQLT_CHR, 64)
830
+ stmt.bindByPos(2, OCI8::SQLT_RDD, rid)
831
+ do_ocicall(@ctx) { stmt.execute(@svc, 1, OCI_DEFAULT) }
832
+ str_rid = b.get()
833
+ stmt.free()
834
+ str_rid
835
+ end
836
+ end
837
+
838
+ def prefetch_rows=(rows)
839
+ @stmt.attrSet(OCI_ATTR_PREFETCH_ROWS, rows)
840
+ end
841
+
842
+ private
843
+
844
+ def bind_or_define(bind_type, key, val, type, length, precision, scale, strict_check)
845
+ if type.nil?
846
+ if val.nil?
847
+ raise "bind type is not given." if type.nil?
848
+ else
849
+ if val.class == Class
850
+ type = val
851
+ val = nil
852
+ else
853
+ type = val.class
854
+ end
855
+ end
856
+ end
857
+
858
+ binder = OCI8::BindType::Mapping[type]
859
+ if binder
860
+ type, val, option = binder.fix_type(@env, val, length, precision, scale)
861
+ else
862
+ if strict_check
863
+ raise "unsupported datatype: #{SQLT_NAMES[type] ? SQLT_NAMES[type] : type}"
864
+ else
865
+ option = length
866
+ end
867
+ end
868
+
869
+ case bind_type
870
+ when :bind
871
+ if key.is_a? Fixnum
872
+ b = @stmt.bindByPos(key, type, option)
873
+ else
874
+ b = @stmt.bindByName(key, type, option)
875
+ end
876
+ when :define
877
+ b = @stmt.defineByPos(key, type, option)
878
+ end
879
+ b.set_handle(@env, @svc, @ctx)
880
+
881
+ if binder && binder.respond_to?(:decorate)
882
+ # decorate the bind handle.
883
+ binder.decorate(b)
884
+ end
885
+
886
+ b.set(val) unless val.nil?
887
+ b
888
+ end # bind_or_define
889
+
890
+ def define_columns
891
+ num_cols = @stmt.attrGet(OCI_ATTR_PARAM_COUNT)
892
+ 1.upto(num_cols) do |i|
893
+ @parms[i - 1] = @stmt.paramGet(i)
894
+ end
895
+ @defns = Array.new(@parms.size) if @defns.nil?
896
+ 1.upto(num_cols) do |i|
897
+ @defns[i] = define_a_column(i) if @defns[i].nil?
898
+ end
899
+ num_cols
900
+ end # define_columns
901
+
902
+ def define_a_column(i)
903
+ p = @parms[i - 1]
904
+ datatype = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_DATA_TYPE) }
905
+ datasize = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_DATA_SIZE) }
906
+ precision = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_PRECISION) }
907
+ scale = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_SCALE) }
908
+ csfrm = nil
909
+
910
+ case datatype
911
+ when SQLT_CHR, SQLT_AFC
912
+ # character size may become large on character set conversion.
913
+ # The length of a half-width kana is one in Shift_JIS, two in EUC-JP,
914
+ # three in UTF-8.
915
+ datasize *= 3
916
+ when SQLT_LNG, SQLT_LBI
917
+ datasize = @ctx[OCI8::Util::CTX_LONG_READ_LEN]
918
+ when SQLT_CLOB
919
+ datatype = :nclob if p.attrGet(OCI_ATTR_CHARSET_FORM) == SQLCS_NCHAR
920
+ when SQLT_BIN
921
+ datasize *= 2 if OCI8::BindType::Mapping[datatype] == OCI8::BindType::String
922
+ when SQLT_RDD
923
+ datasize = 64
924
+ end
925
+
926
+ bind_or_define(:define, i, nil, datatype, datasize, precision, scale, true)
927
+ end # define_a_column
928
+
929
+ def bind_params(*bindvars)
930
+ bindvars.each_with_index do |val, i|
931
+ if val.is_a? Array
932
+ bind_param(i + 1, val[0], val[1], val[2])
933
+ else
934
+ bind_param(i + 1, val)
935
+ end
936
+ end
937
+ end # bind_params
938
+
939
+ def fetch_a_row
940
+ @defns.each do |d|
941
+ d.pre_fetch_hook if d.respond_to? :pre_fetch_hook
942
+ end
943
+ res = do_ocicall(@ctx) { @stmt.fetch() }
944
+ return nil if res.nil?
945
+ res.collect do |r| r.get() end
946
+ end # fetch_a_row
947
+
948
+ def fetch_a_hash_row
949
+ if rs = fetch_a_row()
950
+ ret = {}
951
+ @parms.each do |p|
952
+ ret[p.attrGet(OCI_ATTR_NAME)] = rs.shift
953
+ end
954
+ ret
955
+ else
956
+ nil
957
+ end
958
+ end # fetch_a_hash_row
959
+
960
+ def free_defns
961
+ unless @defns.nil?
962
+ @defns.each do |b|
963
+ b.free() unless b.nil?
964
+ end
965
+ end
966
+ @defns = nil
967
+ end # free_defns
968
+
969
+ def free_binds
970
+ unless @binds.nil?
971
+ @binds.each_value do |b|
972
+ b.free()
973
+ end
974
+ end
975
+ @binds = nil
976
+ end # free_binds
977
+ end # OCI8::Cursor
978
+
979
+ class LOB
980
+ attr :pos
981
+ def initialize(svc, val)
982
+ svc = svc.instance_variable_get(:@svc) if svc.is_a? OCI8
983
+ raise "invalid argument" unless svc.is_a? OCISvcCtx
984
+ @env = svc.instance_variable_get(:@env)
985
+ @svc = svc
986
+ @csid = 0
987
+ @pos = 0
988
+ if val.is_a? OCILobLocator
989
+ @locator = val
990
+ else
991
+ @locator = @env.alloc(OCILobLocator)
992
+ @locator.create_temporary(@svc, @csid, @csfrm, @lobtype, false, nil)
993
+ val.nil? || write(val.to_s)
994
+ end
995
+ end
996
+
997
+ def available?
998
+ @locator.is_initialized?(@env)
999
+ end
1000
+
1001
+ def truncate(len)
1002
+ raise "uninitialized LOB" unless available?
1003
+ @locator.trim(@svc, len)
1004
+ self
1005
+ end
1006
+
1007
+ def read(readlen = nil)
1008
+ rest = self.size - @pos
1009
+ return nil if rest == 0 # eof.
1010
+ if readlen.nil? or readlen > rest
1011
+ readlen = rest # read until EOF.
1012
+ end
1013
+ begin
1014
+ rv = @locator.read(@svc, @pos + 1, readlen, @csid, @csfrm)
1015
+ rescue OCIError
1016
+ raise if $!.code != 22289
1017
+ # ORA-22289: cannot perform FILEREAD operation on an unopened file or LOB.
1018
+ open
1019
+ retry
1020
+ end
1021
+ @pos += readlen
1022
+ rv
1023
+ end
1024
+
1025
+ def write(data)
1026
+ raise "uninitialized LOB" unless available?
1027
+ size = @locator.write(@svc, @pos + 1, data, @csid, @csfrm)
1028
+ @pos += size
1029
+ size
1030
+ end
1031
+
1032
+ def size
1033
+ raise "uninitialized LOB" unless available?
1034
+ begin
1035
+ rv = @locator.getLength(@svc)
1036
+ rescue OCIError
1037
+ raise if $!.code != 22289
1038
+ # ORA-22289: cannot perform FILEREAD operation on an unopened file or LOB.
1039
+ open
1040
+ retry
1041
+ end
1042
+ rv
1043
+ end
1044
+
1045
+ def size=(len)
1046
+ raise "uninitialized LOB" unless available?
1047
+ @locator.trim(@svc, len)
1048
+ len
1049
+ end
1050
+
1051
+ def chunk_size # in bytes.
1052
+ raise "uninitialized LOB" unless available?
1053
+ @locator.getChunkSize(@svc)
1054
+ end
1055
+
1056
+ def eof?
1057
+ @pos == size
1058
+ end
1059
+
1060
+ def tell
1061
+ @pos
1062
+ end
1063
+
1064
+ def seek(pos, whence = IO::SEEK_SET)
1065
+ length = size
1066
+ case whence
1067
+ when IO::SEEK_SET
1068
+ @pos = pos
1069
+ when IO::SEEK_CUR
1070
+ @pos += pos
1071
+ when IO::SEEK_END
1072
+ @pos = length + pos
1073
+ end
1074
+ @pos = length if @pos >= length
1075
+ @pos = 0 if @pos < 0
1076
+ self
1077
+ end
1078
+
1079
+ def rewind
1080
+ @pos = 0
1081
+ self
1082
+ end
1083
+
1084
+ def close
1085
+ @locator.free()
1086
+ end
1087
+
1088
+ end
1089
+
1090
+ class BLOB < LOB
1091
+ def initialize(*arg)
1092
+ @lobtype = 1
1093
+ @csfrm = SQLCS_IMPLICIT
1094
+ super(*arg)
1095
+ end
1096
+ end
1097
+
1098
+ class CLOB < LOB
1099
+ def initialize(*arg)
1100
+ @lobtype = 2
1101
+ @csfrm = SQLCS_IMPLICIT
1102
+ super(*arg)
1103
+ end
1104
+ end
1105
+
1106
+ class NCLOB < LOB
1107
+ def initialize(*arg)
1108
+ @lobtype = 2
1109
+ @csfrm = SQLCS_NCHAR
1110
+ super(*arg)
1111
+ end
1112
+ end
1113
+
1114
+ class BFILE < LOB
1115
+ attr_reader :dir_alias
1116
+ attr_reader :filename
1117
+ def initialize(svc, locator)
1118
+ raise "invalid argument" unless svc.is_a? OCISvcCtx
1119
+ raise "invalid argument" unless locator.is_a? OCIFileLocator
1120
+ @env = svc.instance_variable_get(:@env)
1121
+ @svc = svc
1122
+ @locator = locator
1123
+ @pos = 0
1124
+ @dir_alias, @filename = @locator.name(@env)
1125
+ end
1126
+
1127
+ def dir_alias=(val)
1128
+ @locator.set_name(@env, val, @filename)
1129
+ @dir_alias = val
1130
+ end
1131
+
1132
+ def filename=(val)
1133
+ @locator.set_name(@env, @dir_alias, val)
1134
+ @filename = val
1135
+ end
1136
+
1137
+ def truncate(len)
1138
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1139
+ end
1140
+ def write(data)
1141
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1142
+ end
1143
+ def size=(len)
1144
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1145
+ end
1146
+
1147
+ def open
1148
+ begin
1149
+ @locator.open(@svc, :file_readonly)
1150
+ rescue OCIError
1151
+ raise if $!.code != 22290
1152
+ # ORA-22290: operation would exceed the maximum number of opened files or LOBs.
1153
+ @svc.close_all_files
1154
+ @locator.open(@svc, :file_readonly)
1155
+ end
1156
+ end
1157
+
1158
+ def exists?
1159
+ @locator.exists?(@svc)
1160
+ end
1161
+ end
1162
+
1163
+ # bind or explicitly define
1164
+ BindType::Mapping[::String] = BindType::String
1165
+ BindType::Mapping[::OCI8::RAW] = BindType::RAW
1166
+ BindType::Mapping[::OraDate] = BindType::OraDate
1167
+ BindType::Mapping[::Time] = BindType::Time
1168
+ BindType::Mapping[::Date] = BindType::Date
1169
+ BindType::Mapping[::DateTime] = BindType::DateTime if defined? DateTime
1170
+ BindType::Mapping[::OCIRowid] = BindType::OCIRowid
1171
+ BindType::Mapping[::OCI8::BLOB] = BindType::BLOB
1172
+ BindType::Mapping[::OCI8::CLOB] = BindType::CLOB
1173
+ BindType::Mapping[::OCI8::NCLOB] = BindType::NCLOB
1174
+ BindType::Mapping[::OCI8::BFILE] = BindType::BFILE
1175
+ BindType::Mapping[::OCI8::Cursor] = BindType::Cursor
1176
+
1177
+ # implicitly define
1178
+
1179
+ # datatype type size prec scale
1180
+ # -------------------------------------------------
1181
+ # CHAR(1) SQLT_AFC 1 0 0
1182
+ # CHAR(10) SQLT_AFC 10 0 0
1183
+ BindType::Mapping[OCI8::SQLT_AFC] = BindType::String
1184
+
1185
+ # datatype type size prec scale
1186
+ # -------------------------------------------------
1187
+ # VARCHAR(1) SQLT_CHR 1 0 0
1188
+ # VARCHAR(10) SQLT_CHR 10 0 0
1189
+ # VARCHAR2(1) SQLT_CHR 1 0 0
1190
+ # VARCHAR2(10) SQLT_CHR 10 0 0
1191
+ BindType::Mapping[OCI8::SQLT_CHR] = BindType::String
1192
+
1193
+ # datatype type size prec scale
1194
+ # -------------------------------------------------
1195
+ # RAW(1) SQLT_BIN 1 0 0
1196
+ # RAW(10) SQLT_BIN 10 0 0
1197
+ BindType::Mapping[OCI8::SQLT_BIN] = BindType::RAW
1198
+
1199
+ # datatype type size prec scale
1200
+ # -------------------------------------------------
1201
+ # LONG SQLT_LNG 0 0 0
1202
+ BindType::Mapping[OCI8::SQLT_LNG] = BindType::String
1203
+
1204
+ # datatype type size prec scale
1205
+ # -------------------------------------------------
1206
+ # LONG RAW SQLT_LBI 0 0 0
1207
+ BindType::Mapping[OCI8::SQLT_LBI] = BindType::RAW
1208
+
1209
+ # datatype type size prec scale
1210
+ # -------------------------------------------------
1211
+ # CLOB SQLT_CLOB 4000 0 0
1212
+ BindType::Mapping[OCI8::SQLT_CLOB] = BindType::CLOB
1213
+ BindType::Mapping[:nclob] = BindType::NCLOB # if OCI_ATTR_CHARSET_FORM is SQLCS_NCHAR.
1214
+
1215
+ # datatype type size prec scale
1216
+ # -------------------------------------------------
1217
+ # BLOB SQLT_BLOB 4000 0 0
1218
+ BindType::Mapping[OCI8::SQLT_BLOB] = BindType::BLOB
1219
+
1220
+ # datatype type size prec scale
1221
+ # -------------------------------------------------
1222
+ # BFILE SQLT_BFILE 4000 0 0
1223
+ BindType::Mapping[OCI8::SQLT_BFILE] = BindType::BFILE
1224
+
1225
+ # datatype type size prec scale
1226
+ # -------------------------------------------------
1227
+ # DATE SQLT_DAT 7 0 0
1228
+ BindType::Mapping[OCI8::SQLT_DAT] = BindType::OraDate
1229
+
1230
+ BindType::Mapping[OCI8::SQLT_TIMESTAMP] = BindType::OraDate
1231
+
1232
+ # datatype type size prec scale
1233
+ # -------------------------------------------------
1234
+ # ROWID SQLT_RDD 4 0 0
1235
+ BindType::Mapping[OCI8::SQLT_RDD] = BindType::String
1236
+
1237
+ # datatype type size prec scale
1238
+ # -----------------------------------------------------
1239
+ # FLOAT SQLT_NUM 22 126 -127
1240
+ # FLOAT(1) SQLT_NUM 22 1 -127
1241
+ # FLOAT(126) SQLT_NUM 22 126 -127
1242
+ # DOUBLE PRECISION SQLT_NUM 22 126 -127
1243
+ # REAL SQLT_NUM 22 63 -127
1244
+ # calculated value SQLT_NUM 22 0 0
1245
+ # NUMBER SQLT_NUM 22 0 0 (Oracle 9.2.0.2 or below)
1246
+ # NUMBER SQLT_NUM 22 0 -127 (Oracle 9.2.0.3 or above)
1247
+ # NUMBER(1) SQLT_NUM 22 1 0
1248
+ # NUMBER(38) SQLT_NUM 22 38 0
1249
+ # NUMBER(1, 0) SQLT_NUM 22 1 0
1250
+ # NUMBER(38, 0) SQLT_NUM 22 38 0
1251
+ # NUMERIC SQLT_NUM 22 38 0
1252
+ # INT SQLT_NUM 22 38 0
1253
+ # INTEGER SQLT_NUM 22 38 0
1254
+ # SMALLINT SQLT_NUM 22 38 0
1255
+ BindType::Mapping[OCI8::SQLT_NUM] = BindType::Number
1256
+
1257
+ # This parameter specify the ruby datatype for
1258
+ # calculated number values whose precision is unknown in advance.
1259
+ # select col1 * 1.1 from tab1;
1260
+ # For Oracle 9.2.0.2 or below, this is also used for NUMBER
1261
+ # datatypes that have no explicit setting of their precision
1262
+ # and scale.
1263
+ BindType::Mapping[:number_unknown_prec] = BindType::Float
1264
+
1265
+ # This parameter specify the ruby datatype for NUMBER datatypes
1266
+ # that have no explicit setting of their precision and scale.
1267
+ # create table tab1 (col1 number);
1268
+ # select col1 from tab1;
1269
+ # note: This is available only on Oracle 9.2.0.3 or above.
1270
+ # see: Oracle 9.2.0.x Patch Set Notes.
1271
+ BindType::Mapping[:number_no_prec_setting] = BindType::Float
1272
+
1273
+ # datatype type size prec scale
1274
+ # -------------------------------------------------
1275
+ # BINARY FLOAT SQLT_IBFLOAT 4 0 0
1276
+ # BINARY DOUBLE SQLT_IBDOUBLE 8 0 0
1277
+ if defined? BindType::BinaryDouble
1278
+ BindType::Mapping[OCI8::SQLT_IBFLOAT] = BindType::BinaryDouble
1279
+ BindType::Mapping[OCI8::SQLT_IBDOUBLE] = BindType::BinaryDouble
1280
+ else
1281
+ BindType::Mapping[OCI8::SQLT_IBFLOAT] = BindType::Float
1282
+ BindType::Mapping[OCI8::SQLT_IBDOUBLE] = BindType::Float
1283
+ end
1284
+
1285
+ # cursor in result set.
1286
+ BindType::Mapping[SQLT_RSET] = BindType::Cursor
1287
+ end # OCI8
1288
+
1289
+ class OraDate
1290
+ def to_time
1291
+ begin
1292
+ Time.local(year, month, day, hour, minute, second)
1293
+ rescue ArgumentError
1294
+ msg = format("out of range of Time (expect between 1970-01-01 00:00:00 UTC and 2037-12-31 23:59:59, but %04d-%02d-%02d %02d:%02d:%02d %s)", year, month, day, hour, minute, second, Time.at(0).zone)
1295
+ raise RangeError.new(msg)
1296
+ end
1297
+ end
1298
+
1299
+ def to_date
1300
+ Date.new(year, month, day)
1301
+ end
1302
+
1303
+ if defined? DateTime # ruby 1.8.0 or upper
1304
+ def to_datetime
1305
+ DateTime.new(year, month, day, hour, minute, second)
1306
+ end
1307
+ end
1308
+
1309
+ def yaml_initialize(type, val) # :nodoc:
1310
+ initialize(*val.split(/[ -\/:]+/).collect do |i| i.to_i end)
1311
+ end
1312
+
1313
+ def to_yaml(opts = {}) # :nodoc:
1314
+ YAML.quick_emit(object_id, opts) do |out|
1315
+ out.scalar(taguri, self.to_s, :plain)
1316
+ end
1317
+ end
1318
+
1319
+ def to_json(options=nil) # :nodoc:
1320
+ to_datetime.to_json(options)
1321
+ end
1322
+ end
1323
+
1324
+ class OraNumber
1325
+ def yaml_initialize(type, val) # :nodoc:
1326
+ initialize(val)
1327
+ end
1328
+
1329
+ def to_yaml(opts = {}) # :nodoc:
1330
+ YAML.quick_emit(object_id, opts) do |out|
1331
+ out.scalar(taguri, self.to_s, :plain)
1332
+ end
1333
+ end
1334
+
1335
+ def to_json(options=nil) # :nodoc:
1336
+ to_s
1337
+ end
1338
+ end
1339
+
1340
+
1341
+ #
1342
+ # OCI8::Metadata::Column
1343
+ #
1344
+ class OCI8
1345
+ module Metadata
1346
+
1347
+ # Abstract super class for Metadata classes.
1348
+ class Base
1349
+ # This class's code was copied from svn trunk whick will be ruby-oci8 2.0.
1350
+
1351
+ # SQLT values to name
1352
+ DATA_TYPE_MAP = {} # :nodoc:
1353
+ TYPE_PROC_MAP = {} # :nodoc:
1354
+
1355
+ # SQLT_CHR
1356
+ DATA_TYPE_MAP[1] = :varchar2
1357
+ TYPE_PROC_MAP[1] = Proc.new do |p|
1358
+ if p.charset_form == :nchar
1359
+ "NVARCHAR2(#{p.char_size})"
1360
+ else
1361
+ if (p.respond_to? :char_used?) && (p.char_used?)
1362
+ "VARCHAR2(#{p.char_size} CHAR)"
1363
+ else
1364
+ "VARCHAR2(#{p.data_size})"
1365
+ end
1366
+ end
1367
+ end
1368
+
1369
+ # SQLT_NUM
1370
+ DATA_TYPE_MAP[2] = :number
1371
+ TYPE_PROC_MAP[2] = Proc.new do |p|
1372
+ begin
1373
+ case p.scale
1374
+ when -127
1375
+ case p.precision
1376
+ when 0
1377
+ "NUMBER"
1378
+ when 126
1379
+ "FLOAT"
1380
+ else
1381
+ "FLOAT(#{p.precision})"
1382
+ end
1383
+ when 0
1384
+ case p.precision
1385
+ when 0
1386
+ "NUMBER"
1387
+ else
1388
+ "NUMBER(#{p.precision})"
1389
+ end
1390
+ else
1391
+ "NUMBER(#{p.precision},#{p.scale})"
1392
+ end
1393
+ rescue OCIError
1394
+ "NUMBER"
1395
+ end
1396
+ end
1397
+
1398
+ # SQLT_LNG
1399
+ DATA_TYPE_MAP[8] = :long
1400
+ TYPE_PROC_MAP[8] = "LONG"
1401
+
1402
+ # SQLT_DAT
1403
+ DATA_TYPE_MAP[12] = :date
1404
+ TYPE_PROC_MAP[12] = "DATE"
1405
+
1406
+ # SQLT_BIN
1407
+ DATA_TYPE_MAP[23] = :raw
1408
+ TYPE_PROC_MAP[23] = Proc.new do |p|
1409
+ "RAW(#{p.data_size})"
1410
+ end
1411
+
1412
+ # SQLT_LBI
1413
+ DATA_TYPE_MAP[24] = :long_raw
1414
+ TYPE_PROC_MAP[24] = "LONG RAW"
1415
+
1416
+ # SQLT_AFC
1417
+ DATA_TYPE_MAP[96] = :char
1418
+ TYPE_PROC_MAP[96] = Proc.new do |p|
1419
+ if p.charset_form == :nchar
1420
+ "NCHAR(#{p.char_size})"
1421
+ else
1422
+ if (p.respond_to? :char_used?) && (p.char_used?)
1423
+ "CHAR(#{p.char_size} CHAR)"
1424
+ else
1425
+ "CHAR(#{p.data_size})"
1426
+ end
1427
+ end
1428
+ end
1429
+
1430
+ # SQLT_IBFLOAT
1431
+ DATA_TYPE_MAP[100] = :binary_float
1432
+ TYPE_PROC_MAP[100] = "BINARY_FLOAT"
1433
+
1434
+ # SQLT_IBDOUBLE
1435
+ DATA_TYPE_MAP[101] = :binary_double
1436
+ TYPE_PROC_MAP[101] = "BINARY_DOUBLE"
1437
+
1438
+ # SQLT_RDD
1439
+ DATA_TYPE_MAP[104] = :rowid
1440
+ TYPE_PROC_MAP[104] = "ROWID"
1441
+
1442
+ # SQLT_NTY
1443
+ DATA_TYPE_MAP[108] = :named_type
1444
+ TYPE_PROC_MAP[108] = "Object"
1445
+
1446
+ # SQLT_REF
1447
+ DATA_TYPE_MAP[110] = :ref
1448
+ TYPE_PROC_MAP[110] = "REF"
1449
+
1450
+ # SQLT_CLOB
1451
+ DATA_TYPE_MAP[112] = :clob
1452
+ TYPE_PROC_MAP[112] = Proc.new do |p|
1453
+ if p.charset_form == :nchar
1454
+ "NCLOB"
1455
+ else
1456
+ "CLOB"
1457
+ end
1458
+ end
1459
+
1460
+ # SQLT_BLOB
1461
+ DATA_TYPE_MAP[113] = :blob
1462
+ TYPE_PROC_MAP[113] = "BLOB"
1463
+
1464
+ # SQLT_BFILE
1465
+ DATA_TYPE_MAP[114] = :bfile
1466
+ TYPE_PROC_MAP[114] = "BFILE"
1467
+
1468
+ # SQLT_TIMESTAMP
1469
+ DATA_TYPE_MAP[187] = :timestamp
1470
+ TYPE_PROC_MAP[187] = Proc.new do |p|
1471
+ fsprecision = p.fsprecision
1472
+ if fsprecision == 6
1473
+ "TIMESTAMP"
1474
+ else
1475
+ "TIMESTAMP(#{fsprecision})"
1476
+ end
1477
+ end
1478
+
1479
+ # SQLT_TIMESTAMP_TZ
1480
+ DATA_TYPE_MAP[188] = :timestamp_tz
1481
+ TYPE_PROC_MAP[188] = Proc.new do |p|
1482
+ fsprecision = p.fsprecision
1483
+ if fsprecision == 6
1484
+ "TIMESTAMP WITH TIME ZONE"
1485
+ else
1486
+ "TIMESTAMP(#{fsprecision}) WITH TIME ZONE"
1487
+ end
1488
+ end
1489
+
1490
+ # SQLT_INTERVAL_YM
1491
+ DATA_TYPE_MAP[189] = :interval_ym
1492
+ TYPE_PROC_MAP[189] = Proc.new do |p|
1493
+ lfprecision = p.lfprecision
1494
+ if lfprecision == 2
1495
+ "INTERVAL YEAR TO MONTH"
1496
+ else
1497
+ "INTERVAL YEAR(#{lfprecision}) TO MONTH"
1498
+ end
1499
+ end
1500
+
1501
+ # SQLT_INTERVAL_DS
1502
+ DATA_TYPE_MAP[190] = :interval_ds
1503
+ TYPE_PROC_MAP[190] = Proc.new do |p|
1504
+ lfprecision = p.lfprecision
1505
+ fsprecision = p.fsprecision
1506
+ if lfprecision == 2 && fsprecision == 6
1507
+ "INTERVAL DAY TO SECOND"
1508
+ else
1509
+ "INTERVAL DAY(#{lfprecision}) TO SECOND(#{fsprecision})"
1510
+ end
1511
+ end
1512
+
1513
+ # SQLT_TIMESTAMP_LTZ
1514
+ DATA_TYPE_MAP[232] = :timestamp_ltz
1515
+ TYPE_PROC_MAP[232] = Proc.new do |p|
1516
+ fsprecision = p.fsprecision
1517
+ if fsprecision == 6
1518
+ "TIMESTAMP WITH LOCAL TIME ZONE"
1519
+ else
1520
+ "TIMESTAMP(#{fsprecision}) WITH LOCAL TIME ZONE"
1521
+ end
1522
+ end
1523
+
1524
+ def inspect # :nodoc:
1525
+ "#<#{self.class.name}:(#{@obj_id}) #{@obj_schema}.#{@obj_name}>"
1526
+ end
1527
+
1528
+ private
1529
+
1530
+ def __data_type(p)
1531
+ DATA_TYPE_MAP[p.attrGet(OCI_ATTR_DATA_TYPE)] || p.attrGet(OCI_ATTR_DATA_TYPE)
1532
+ end
1533
+
1534
+ def __type_string(p)
1535
+ type = TYPE_PROC_MAP[p.attrGet(OCI_ATTR_DATA_TYPE)] || "unknown(#{p.attrGet(OCI_ATTR_DATA_TYPE)})"
1536
+ type = type.call(self) if type.is_a? Proc
1537
+ if respond_to?(:nullable?) && !nullable?
1538
+ type + " NOT NULL"
1539
+ else
1540
+ type
1541
+ end
1542
+ end
1543
+
1544
+ def initialize_table_or_view(param)
1545
+ @num_cols = param.attrGet(OCI_ATTR_NUM_COLS)
1546
+ @obj_id = param.attrGet(OCI_ATTR_OBJ_ID)
1547
+ @obj_name = param.attrGet(OCI_ATTR_OBJ_NAME)
1548
+ @obj_schema = param.attrGet(OCI_ATTR_OBJ_SCHEMA)
1549
+ colparam = param.attrGet(OCI_ATTR_LIST_COLUMNS)
1550
+ @columns = []
1551
+ 1.upto @num_cols do |i|
1552
+ @columns << OCI8::Metadata::Column.new(colparam.paramGet(i))
1553
+ end
1554
+ end
1555
+ end
1556
+
1557
+ class Table < Base
1558
+ attr_reader :num_cols
1559
+ attr_reader :obj_name
1560
+ attr_reader :obj_schema
1561
+ attr_reader :columns
1562
+
1563
+ def initialize(param)
1564
+ initialize_table_or_view(param)
1565
+ end
1566
+ end
1567
+
1568
+ class View < Base
1569
+ attr_reader :num_cols
1570
+ attr_reader :obj_name
1571
+ attr_reader :obj_schema
1572
+ attr_reader :columns
1573
+
1574
+ def initialize(param)
1575
+ initialize_table_or_view(param)
1576
+ end
1577
+ end
1578
+
1579
+ class Column < Base
1580
+ attr_reader :name
1581
+ attr_reader :type_string
1582
+ attr_reader :data_type
1583
+ attr_reader :charset_form
1584
+ def nullable?; @nullable; end
1585
+
1586
+ # string data type
1587
+ def char_used?; @char_used; end if defined? OCI_ATTR_CHAR_USED
1588
+ attr_reader :char_size if defined? OCI_ATTR_CHAR_SIZE
1589
+ attr_reader :data_size
1590
+ attr_reader :charset_id
1591
+
1592
+ # number data type
1593
+ attr_reader :precision
1594
+ attr_reader :scale
1595
+
1596
+ # interval
1597
+ if defined? OCI_ATTR_FSPRECISION and defined? OCI_ATTR_LFPRECISION
1598
+ # Oracle 8i or upper has OCI_ATTR_FSPRECISION and OCI_ATTR_LFPRECISION
1599
+ @@is_fsprecision_available = true
1600
+ else
1601
+ @@is_fsprecision_available = false
1602
+ end
1603
+ attr_reader :fsprecision
1604
+ attr_reader :lfprecision
1605
+
1606
+ def initialize(param)
1607
+ @name = param.attrGet(OCI_ATTR_NAME)
1608
+ @data_type = __data_type(param)
1609
+
1610
+ @data_size = param.attrGet(OCI_ATTR_DATA_SIZE)
1611
+ @char_used = param.attrGet(OCI_ATTR_CHAR_USED) if defined? OCI_ATTR_CHAR_USED
1612
+ @char_size = param.attrGet(OCI_ATTR_CHAR_SIZE) if defined? OCI_ATTR_CHAR_SIZE
1613
+
1614
+ @precision = param.attrGet(OCI_ATTR_PRECISION)
1615
+ @scale = param.attrGet(OCI_ATTR_SCALE)
1616
+ @nullable = param.attrGet(OCI_ATTR_IS_NULL)
1617
+ @charset_id = param.attrGet(OCI_ATTR_CHARSET_ID)
1618
+ @charset_form = case param.attrGet(OCI_ATTR_CHARSET_FORM)
1619
+ when 0: nil
1620
+ when 1; :implicit
1621
+ when 2; :nchar
1622
+ when 3; :explicit
1623
+ when 4; :flexible
1624
+ when 5; :lit_null
1625
+ else raise "unknown charset_form #{param.attrGet(OCI_ATTR_CHARSET_FORM)}"
1626
+ end
1627
+
1628
+ @fsprecision = nil
1629
+ @lfprecision = nil
1630
+ if @@is_fsprecision_available
1631
+ begin
1632
+ @fsprecision = param.attrGet(OCI_ATTR_FSPRECISION)
1633
+ @lfprecision = param.attrGet(OCI_ATTR_LFPRECISION)
1634
+ rescue OCIError
1635
+ raise if $!.code != 24316 # ORA-24316: illegal handle type
1636
+ # Oracle 8i could not use OCI_ATTR_FSPRECISION and
1637
+ # OCI_ATTR_LFPRECISION even though it defines these
1638
+ # constants in oci.h.
1639
+ @@is_fsprecision_available = false
1640
+ end
1641
+ end
1642
+
1643
+ @type_string = __type_string(param)
1644
+ end
1645
+
1646
+ def to_s
1647
+ %Q{"#{@name}" #{@type_string}}
1648
+ end
1649
+
1650
+ def inspect # :nodoc:
1651
+ "#<#{self.class.name}: #{@name} #{@type_string}>"
1652
+ end
1653
+ end
1654
+ end
1655
+ end