ruby-oci8 1.0.6-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
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 = '1.0.6'
59
+ CLIENT_VERSION = '1020'
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