ruby-oci8 1.0.2-i386-mswin32

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 (49) hide show
  1. data/ChangeLog +569 -0
  2. data/Makefile +51 -0
  3. data/NEWS +322 -0
  4. data/README +415 -0
  5. data/VERSION +1 -0
  6. data/dist-files +70 -0
  7. data/doc/api.en.html +527 -0
  8. data/doc/api.en.rd +554 -0
  9. data/doc/api.ja.html +525 -0
  10. data/doc/api.ja.rd +557 -0
  11. data/doc/manual.css +35 -0
  12. data/ext/oci8/oci8lib.so +0 -0
  13. data/lib/DBD/OCI8/OCI8.rb +549 -0
  14. data/lib/oci8.rb +1605 -0
  15. data/lib/oci8.rb.in +1605 -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 +43 -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 +317 -0
  43. data/test/test_dbi_clob.rb +56 -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. metadata +97 -0
data/lib/oci8.rb.in ADDED
@@ -0,0 +1,1605 @@
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
+ 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
+ @env = env
683
+ @svc = svc
684
+ @ctx = ctx
685
+ @binds = nil
686
+ @parms = []
687
+ @defns = nil
688
+ if stmt.nil?
689
+ @stmt = @env.alloc(OCIStmt)
690
+ @stmttype = nil
691
+ else
692
+ @stmt = stmt
693
+ @stmttype = @stmt.attrGet(OCI_ATTR_STMT_TYPE)
694
+ define_columns()
695
+ end
696
+ end # initialize
697
+
698
+ def parse(sql)
699
+ free_binds()
700
+ @parms = []
701
+ @stmt.prepare(sql)
702
+ @stmttype = do_ocicall(@ctx) { @stmt.attrGet(OCI_ATTR_STMT_TYPE) }
703
+ end # parse
704
+
705
+ def define(pos, type, length = nil)
706
+ @defns = [] if @defns.nil?
707
+ if type == String and length.nil?
708
+ length = 4000
709
+ end
710
+ b = bind_or_define(:define, pos, nil, type, length, nil, nil, false)
711
+ @defns[pos].free() unless @defns[pos].nil?
712
+ @defns[pos] = b
713
+ self
714
+ end # define
715
+
716
+ def bind_param(key, val, type = nil, length = nil)
717
+ @binds = {} if @binds.nil?
718
+ b = bind_or_define(:bind, key, val, type, length, nil, nil, false)
719
+ @binds[key].free() unless @binds[key].nil?
720
+ @binds[key] = b
721
+ self
722
+ end # bind_param
723
+
724
+ # get bind value
725
+ def [](key)
726
+ if @binds.nil? or @binds[key].nil?
727
+ return nil
728
+ end
729
+ @binds[key].get()
730
+ end
731
+
732
+ # set bind value
733
+ def []=(key, val)
734
+ if @binds.nil? or @binds[key].nil?
735
+ return nil
736
+ end
737
+ @binds[key].set(val)
738
+ end
739
+
740
+ # get bind keys
741
+ def keys
742
+ if @binds.nil?
743
+ []
744
+ else
745
+ @binds.keys
746
+ end
747
+ end
748
+
749
+ def exec(*bindvars)
750
+ bind_params(*bindvars)
751
+ case @stmttype
752
+ when OCI_STMT_SELECT
753
+ do_ocicall(@ctx) { @stmt.execute(@svc, 0, OCI_DEFAULT) }
754
+ define_columns()
755
+ else
756
+ do_ocicall(@ctx) { @stmt.execute(@svc, 1, @ctx[CTX_EXECFLAG]) }
757
+ @stmt.attrGet(OCI_ATTR_ROW_COUNT)
758
+ end
759
+ end # exec
760
+
761
+ def type
762
+ @stmttype
763
+ end
764
+
765
+ def row_count
766
+ @stmt.attrGet(OCI_ATTR_ROW_COUNT)
767
+ end
768
+
769
+ def get_col_names
770
+ @parms.collect do |p|
771
+ do_ocicall(@ctx) { p.attrGet(OCI_ATTR_NAME) }
772
+ end
773
+ end # get_col_names
774
+
775
+ # add alias compatible with 'Oracle7 Module for Ruby'.
776
+ alias getColNames get_col_names
777
+
778
+ def column_metadata
779
+ @parms.collect do |p|
780
+ OCI8::Metadata::Column.new(p)
781
+ end
782
+ end
783
+
784
+ def fetch
785
+ if iterator?
786
+ while ret = fetch_a_row()
787
+ yield(ret)
788
+ end
789
+ else
790
+ fetch_a_row()
791
+ end
792
+ end # fetch
793
+
794
+ def fetch_hash
795
+ if iterator?
796
+ while ret = fetch_a_hash_row()
797
+ yield(ret)
798
+ end
799
+ else
800
+ fetch_a_hash_row
801
+ end
802
+ end # fetch_hash
803
+
804
+ def close
805
+ @env = nil
806
+ @svc = nil
807
+ free_defns()
808
+ free_binds()
809
+ @stmt.free()
810
+ @parms = nil
811
+ @stmttype = nil
812
+ end # close
813
+
814
+ def rowid
815
+ @stmt.attrGet(OCI_ATTR_ROWID)
816
+ end
817
+
818
+ def prefetch_rows=(rows)
819
+ @stmt.attrSet(OCI_ATTR_PREFETCH_ROWS, rows)
820
+ end
821
+
822
+ private
823
+
824
+ def bind_or_define(bind_type, key, val, type, length, precision, scale, strict_check)
825
+ if type.nil?
826
+ if val.nil?
827
+ raise "bind type is not given." if type.nil?
828
+ else
829
+ if val.class == Class
830
+ type = val
831
+ val = nil
832
+ else
833
+ type = val.class
834
+ end
835
+ end
836
+ end
837
+
838
+ binder = OCI8::BindType::Mapping[type]
839
+ if binder
840
+ type, val, option = binder.fix_type(@env, val, length, precision, scale)
841
+ else
842
+ if strict_check
843
+ raise "unsupported datatype: #{SQLT_NAMES[type] ? SQLT_NAMES[type] : type}"
844
+ else
845
+ option = length
846
+ end
847
+ end
848
+
849
+ case bind_type
850
+ when :bind
851
+ if key.is_a? Fixnum
852
+ b = @stmt.bindByPos(key, type, option)
853
+ else
854
+ b = @stmt.bindByName(key, type, option)
855
+ end
856
+ when :define
857
+ b = @stmt.defineByPos(key, type, option)
858
+ end
859
+ b.set_handle(@env, @svc, @ctx)
860
+
861
+ if binder && binder.respond_to?(:decorate)
862
+ # decorate the bind handle.
863
+ binder.decorate(b)
864
+ end
865
+
866
+ b.set(val) unless val.nil?
867
+ b
868
+ end # bind_or_define
869
+
870
+ def define_columns
871
+ num_cols = @stmt.attrGet(OCI_ATTR_PARAM_COUNT)
872
+ 1.upto(num_cols) do |i|
873
+ @parms[i - 1] = @stmt.paramGet(i)
874
+ end
875
+ @defns = Array.new(@parms.size) if @defns.nil?
876
+ 1.upto(num_cols) do |i|
877
+ @defns[i] = define_a_column(i) if @defns[i].nil?
878
+ end
879
+ num_cols
880
+ end # define_columns
881
+
882
+ def define_a_column(i)
883
+ p = @parms[i - 1]
884
+ datatype = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_DATA_TYPE) }
885
+ datasize = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_DATA_SIZE) }
886
+ precision = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_PRECISION) }
887
+ scale = do_ocicall(@ctx) { p.attrGet(OCI_ATTR_SCALE) }
888
+ csfrm = nil
889
+
890
+ case datatype
891
+ when SQLT_CHR, SQLT_AFC
892
+ # character size may become large on character set conversion.
893
+ # The length of a half-width kana is one in Shift_JIS, two in EUC-JP,
894
+ # three in UTF-8.
895
+ datasize *= 3
896
+ when SQLT_LNG, SQLT_LBI
897
+ datasize = @ctx[OCI8::Util::CTX_LONG_READ_LEN]
898
+ when SQLT_CLOB
899
+ datatype = :nclob if p.attrGet(OCI_ATTR_CHARSET_FORM) == SQLCS_NCHAR
900
+ when SQLT_BIN
901
+ datasize *= 2 if OCI8::BindType::Mapping[datatype] == OCI8::BindType::String
902
+ end
903
+
904
+ bind_or_define(:define, i, nil, datatype, datasize, precision, scale, true)
905
+ end # define_a_column
906
+
907
+ def bind_params(*bindvars)
908
+ bindvars.each_with_index do |val, i|
909
+ if val.is_a? Array
910
+ bind_param(i + 1, val[0], val[1], val[2])
911
+ else
912
+ bind_param(i + 1, val)
913
+ end
914
+ end
915
+ end # bind_params
916
+
917
+ def fetch_a_row
918
+ @defns.each do |d|
919
+ d.pre_fetch_hook if d.respond_to? :pre_fetch_hook
920
+ end
921
+ res = do_ocicall(@ctx) { @stmt.fetch() }
922
+ return nil if res.nil?
923
+ res.collect do |r| r.get() end
924
+ end # fetch_a_row
925
+
926
+ def fetch_a_hash_row
927
+ if rs = fetch_a_row()
928
+ ret = {}
929
+ @parms.each do |p|
930
+ ret[p.attrGet(OCI_ATTR_NAME)] = rs.shift
931
+ end
932
+ ret
933
+ else
934
+ nil
935
+ end
936
+ end # fetch_a_hash_row
937
+
938
+ def free_defns
939
+ unless @defns.nil?
940
+ @defns.each do |b|
941
+ b.free() unless b.nil?
942
+ end
943
+ end
944
+ @defns = nil
945
+ end # free_defns
946
+
947
+ def free_binds
948
+ unless @binds.nil?
949
+ @binds.each_value do |b|
950
+ b.free()
951
+ end
952
+ end
953
+ @binds = nil
954
+ end # free_binds
955
+ end # OCI8::Cursor
956
+
957
+ class LOB
958
+ attr :pos
959
+ def initialize(svc, val)
960
+ svc = svc.instance_variable_get(:@svc) if svc.is_a? OCI8
961
+ raise "invalid argument" unless svc.is_a? OCISvcCtx
962
+ @env = svc.instance_variable_get(:@env)
963
+ @svc = svc
964
+ @csid = 0
965
+ @pos = 0
966
+ if val.is_a? OCILobLocator
967
+ @locator = val
968
+ else
969
+ @locator = @env.alloc(OCILobLocator)
970
+ @locator.create_temporary(@svc, @csid, @csfrm, @lobtype, false, nil)
971
+ val.nil? || write(val.to_s)
972
+ end
973
+ end
974
+
975
+ def available?
976
+ @locator.is_initialized?(@env)
977
+ end
978
+
979
+ def truncate(len)
980
+ raise "uninitialized LOB" unless available?
981
+ @locator.trim(@svc, len)
982
+ self
983
+ end
984
+
985
+ def read(readlen = nil)
986
+ rest = self.size - @pos
987
+ return nil if rest == 0 # eof.
988
+ if readlen.nil? or readlen > rest
989
+ readlen = rest # read until EOF.
990
+ end
991
+ begin
992
+ rv = @locator.read(@svc, @pos + 1, readlen, @csid, @csfrm)
993
+ rescue OCIError
994
+ raise if $!.code != 22289
995
+ # ORA-22289: cannot perform FILEREAD operation on an unopened file or LOB.
996
+ open
997
+ retry
998
+ end
999
+ @pos += readlen
1000
+ rv
1001
+ end
1002
+
1003
+ def write(data)
1004
+ raise "uninitialized LOB" unless available?
1005
+ size = @locator.write(@svc, @pos + 1, data, @csid, @csfrm)
1006
+ @pos += size
1007
+ size
1008
+ end
1009
+
1010
+ def size
1011
+ raise "uninitialized LOB" unless available?
1012
+ begin
1013
+ rv = @locator.getLength(@svc)
1014
+ rescue OCIError
1015
+ raise if $!.code != 22289
1016
+ # ORA-22289: cannot perform FILEREAD operation on an unopened file or LOB.
1017
+ open
1018
+ retry
1019
+ end
1020
+ rv
1021
+ end
1022
+
1023
+ def size=(len)
1024
+ raise "uninitialized LOB" unless available?
1025
+ @locator.trim(@svc, len)
1026
+ len
1027
+ end
1028
+
1029
+ def chunk_size # in bytes.
1030
+ raise "uninitialized LOB" unless available?
1031
+ @locator.getChunkSize(@svc)
1032
+ end
1033
+
1034
+ def eof?
1035
+ @pos == size
1036
+ end
1037
+
1038
+ def tell
1039
+ @pos
1040
+ end
1041
+
1042
+ def seek(pos, whence = IO::SEEK_SET)
1043
+ length = size
1044
+ case whence
1045
+ when IO::SEEK_SET
1046
+ @pos = pos
1047
+ when IO::SEEK_CUR
1048
+ @pos += pos
1049
+ when IO::SEEK_END
1050
+ @pos = length + pos
1051
+ end
1052
+ @pos = length if @pos >= length
1053
+ @pos = 0 if @pos < 0
1054
+ self
1055
+ end
1056
+
1057
+ def rewind
1058
+ @pos = 0
1059
+ self
1060
+ end
1061
+
1062
+ def close
1063
+ @locator.free()
1064
+ end
1065
+
1066
+ end
1067
+
1068
+ class BLOB < LOB
1069
+ def initialize(*arg)
1070
+ @lobtype = 1
1071
+ @csfrm = SQLCS_IMPLICIT
1072
+ super(*arg)
1073
+ end
1074
+ end
1075
+
1076
+ class CLOB < LOB
1077
+ def initialize(*arg)
1078
+ @lobtype = 2
1079
+ @csfrm = SQLCS_IMPLICIT
1080
+ super(*arg)
1081
+ end
1082
+ end
1083
+
1084
+ class NCLOB < LOB
1085
+ def initialize(*arg)
1086
+ @lobtype = 2
1087
+ @csfrm = SQLCS_NCHAR
1088
+ super(*arg)
1089
+ end
1090
+ end
1091
+
1092
+ class BFILE < LOB
1093
+ attr_reader :dir_alias
1094
+ attr_reader :filename
1095
+ def initialize(svc, locator)
1096
+ raise "invalid argument" unless svc.is_a? OCISvcCtx
1097
+ raise "invalid argument" unless locator.is_a? OCIFileLocator
1098
+ @env = svc.instance_variable_get(:@env)
1099
+ @svc = svc
1100
+ @locator = locator
1101
+ @pos = 0
1102
+ @dir_alias, @filename = @locator.name(@env)
1103
+ end
1104
+
1105
+ def dir_alias=(val)
1106
+ @locator.set_name(@env, val, @filename)
1107
+ @dir_alias = val
1108
+ end
1109
+
1110
+ def filename=(val)
1111
+ @locator.set_name(@env, @dir_alias, val)
1112
+ @filename = val
1113
+ end
1114
+
1115
+ def truncate(len)
1116
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1117
+ end
1118
+ def write(data)
1119
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1120
+ end
1121
+ def size=(len)
1122
+ raise RuntimeError, "cannot modify a read-only BFILE object"
1123
+ end
1124
+
1125
+ def open
1126
+ begin
1127
+ @locator.open(@svc, :file_readonly)
1128
+ rescue OCIError
1129
+ raise if $!.code != 22290
1130
+ # ORA-22290: operation would exceed the maximum number of opened files or LOBs.
1131
+ @svc.close_all_files
1132
+ @locator.open(@svc, :file_readonly)
1133
+ end
1134
+ end
1135
+
1136
+ def exists?
1137
+ @locator.exists?(@svc)
1138
+ end
1139
+ end
1140
+
1141
+ # bind or explicitly define
1142
+ BindType::Mapping[::String] = BindType::String
1143
+ BindType::Mapping[::OCI8::RAW] = BindType::RAW
1144
+ BindType::Mapping[::OraDate] = BindType::OraDate
1145
+ BindType::Mapping[::Time] = BindType::Time
1146
+ BindType::Mapping[::Date] = BindType::Date
1147
+ BindType::Mapping[::DateTime] = BindType::DateTime if defined? DateTime
1148
+ BindType::Mapping[::OCIRowid] = BindType::OCIRowid
1149
+ BindType::Mapping[::OCI8::BLOB] = BindType::BLOB
1150
+ BindType::Mapping[::OCI8::CLOB] = BindType::CLOB
1151
+ BindType::Mapping[::OCI8::NCLOB] = BindType::NCLOB
1152
+ BindType::Mapping[::OCI8::BFILE] = BindType::BFILE
1153
+ BindType::Mapping[::OCI8::Cursor] = BindType::Cursor
1154
+
1155
+ # implicitly define
1156
+
1157
+ # datatype type size prec scale
1158
+ # -------------------------------------------------
1159
+ # CHAR(1) SQLT_AFC 1 0 0
1160
+ # CHAR(10) SQLT_AFC 10 0 0
1161
+ BindType::Mapping[OCI8::SQLT_AFC] = BindType::String
1162
+
1163
+ # datatype type size prec scale
1164
+ # -------------------------------------------------
1165
+ # VARCHAR(1) SQLT_CHR 1 0 0
1166
+ # VARCHAR(10) SQLT_CHR 10 0 0
1167
+ # VARCHAR2(1) SQLT_CHR 1 0 0
1168
+ # VARCHAR2(10) SQLT_CHR 10 0 0
1169
+ BindType::Mapping[OCI8::SQLT_CHR] = BindType::String
1170
+
1171
+ # datatype type size prec scale
1172
+ # -------------------------------------------------
1173
+ # RAW(1) SQLT_BIN 1 0 0
1174
+ # RAW(10) SQLT_BIN 10 0 0
1175
+ BindType::Mapping[OCI8::SQLT_BIN] = BindType::RAW
1176
+
1177
+ # datatype type size prec scale
1178
+ # -------------------------------------------------
1179
+ # LONG SQLT_LNG 0 0 0
1180
+ BindType::Mapping[OCI8::SQLT_LNG] = BindType::String
1181
+
1182
+ # datatype type size prec scale
1183
+ # -------------------------------------------------
1184
+ # LONG RAW SQLT_LBI 0 0 0
1185
+ BindType::Mapping[OCI8::SQLT_LBI] = BindType::RAW
1186
+
1187
+ # datatype type size prec scale
1188
+ # -------------------------------------------------
1189
+ # CLOB SQLT_CLOB 4000 0 0
1190
+ BindType::Mapping[OCI8::SQLT_CLOB] = BindType::CLOB
1191
+ BindType::Mapping[:nclob] = BindType::NCLOB # if OCI_ATTR_CHARSET_FORM is SQLCS_NCHAR.
1192
+
1193
+ # datatype type size prec scale
1194
+ # -------------------------------------------------
1195
+ # BLOB SQLT_BLOB 4000 0 0
1196
+ BindType::Mapping[OCI8::SQLT_BLOB] = BindType::BLOB
1197
+
1198
+ # datatype type size prec scale
1199
+ # -------------------------------------------------
1200
+ # BFILE SQLT_BFILE 4000 0 0
1201
+ BindType::Mapping[OCI8::SQLT_BFILE] = BindType::BFILE
1202
+
1203
+ # datatype type size prec scale
1204
+ # -------------------------------------------------
1205
+ # DATE SQLT_DAT 7 0 0
1206
+ BindType::Mapping[OCI8::SQLT_DAT] = BindType::OraDate
1207
+
1208
+ BindType::Mapping[OCI8::SQLT_TIMESTAMP] = BindType::OraDate
1209
+
1210
+ # datatype type size prec scale
1211
+ # -------------------------------------------------
1212
+ # ROWID SQLT_RDD 4 0 0
1213
+ BindType::Mapping[OCI8::SQLT_RDD] = BindType::OCIRowid
1214
+
1215
+ # datatype type size prec scale
1216
+ # -----------------------------------------------------
1217
+ # FLOAT SQLT_NUM 22 126 -127
1218
+ # FLOAT(1) SQLT_NUM 22 1 -127
1219
+ # FLOAT(126) SQLT_NUM 22 126 -127
1220
+ # DOUBLE PRECISION SQLT_NUM 22 126 -127
1221
+ # REAL SQLT_NUM 22 63 -127
1222
+ # calculated value SQLT_NUM 22 0 0
1223
+ # NUMBER SQLT_NUM 22 0 0 (Oracle 9.2.0.2 or below)
1224
+ # NUMBER SQLT_NUM 22 0 -127 (Oracle 9.2.0.3 or above)
1225
+ # NUMBER(1) SQLT_NUM 22 1 0
1226
+ # NUMBER(38) SQLT_NUM 22 38 0
1227
+ # NUMBER(1, 0) SQLT_NUM 22 1 0
1228
+ # NUMBER(38, 0) SQLT_NUM 22 38 0
1229
+ # NUMERIC SQLT_NUM 22 38 0
1230
+ # INT SQLT_NUM 22 38 0
1231
+ # INTEGER SQLT_NUM 22 38 0
1232
+ # SMALLINT SQLT_NUM 22 38 0
1233
+ BindType::Mapping[OCI8::SQLT_NUM] = BindType::Number
1234
+
1235
+ # This parameter specify the ruby datatype for
1236
+ # calculated number values whose precision is unknown in advance.
1237
+ # select col1 * 1.1 from tab1;
1238
+ # For Oracle 9.2.0.2 or below, this is also used for NUMBER
1239
+ # datatypes that have no explicit setting of their precision
1240
+ # and scale.
1241
+ BindType::Mapping[:number_unknown_prec] = BindType::Float
1242
+
1243
+ # This parameter specify the ruby datatype for NUMBER datatypes
1244
+ # that have no explicit setting of their precision and scale.
1245
+ # create table tab1 (col1 number);
1246
+ # select col1 from tab1;
1247
+ # note: This is available only on Oracle 9.2.0.3 or above.
1248
+ # see: Oracle 9.2.0.x Patch Set Notes.
1249
+ BindType::Mapping[:number_no_prec_setting] = BindType::Float
1250
+
1251
+ # datatype type size prec scale
1252
+ # -------------------------------------------------
1253
+ # BINARY FLOAT SQLT_IBFLOAT 4 0 0
1254
+ # BINARY DOUBLE SQLT_IBDOUBLE 8 0 0
1255
+ if defined? BindType::BinaryDouble
1256
+ BindType::Mapping[OCI8::SQLT_IBFLOAT] = BindType::BinaryDouble
1257
+ BindType::Mapping[OCI8::SQLT_IBDOUBLE] = BindType::BinaryDouble
1258
+ else
1259
+ BindType::Mapping[OCI8::SQLT_IBFLOAT] = BindType::Float
1260
+ BindType::Mapping[OCI8::SQLT_IBDOUBLE] = BindType::Float
1261
+ end
1262
+
1263
+ # cursor in result set.
1264
+ BindType::Mapping[SQLT_RSET] = BindType::Cursor
1265
+ end # OCI8
1266
+
1267
+ class OraDate
1268
+ def to_time
1269
+ begin
1270
+ Time.local(year, month, day, hour, minute, second)
1271
+ rescue ArgumentError
1272
+ 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)
1273
+ raise RangeError.new(msg)
1274
+ end
1275
+ end
1276
+
1277
+ def to_date
1278
+ Date.new(year, month, day)
1279
+ end
1280
+
1281
+ if defined? DateTime # ruby 1.8.0 or upper
1282
+ def to_datetime
1283
+ DateTime.new(year, month, day, hour, minute, second)
1284
+ end
1285
+ end
1286
+
1287
+ def yaml_initialize(type, val) # :nodoc:
1288
+ initialize(*val.split(/[ -\/:]+/).collect do |i| i.to_i end)
1289
+ end
1290
+
1291
+ def to_yaml(opts = {}) # :nodoc:
1292
+ YAML.quick_emit(object_id, opts) do |out|
1293
+ out.scalar(taguri, self.to_s, :plain)
1294
+ end
1295
+ end
1296
+ end
1297
+
1298
+ class OraNumber
1299
+ def yaml_initialize(type, val) # :nodoc:
1300
+ initialize(val)
1301
+ end
1302
+
1303
+ def to_yaml(opts = {}) # :nodoc:
1304
+ YAML.quick_emit(object_id, opts) do |out|
1305
+ out.scalar(taguri, self.to_s, :plain)
1306
+ end
1307
+ end
1308
+ end
1309
+
1310
+
1311
+ #
1312
+ # OCI8::Metadata::Column
1313
+ #
1314
+ class OCI8
1315
+ module Metadata
1316
+
1317
+ # Abstract super class for Metadata classes.
1318
+ class Base
1319
+ # This class's code was copied from svn trunk whick will be ruby-oci8 2.0.
1320
+
1321
+ # SQLT values to name
1322
+ DATA_TYPE_MAP = {} # :nodoc:
1323
+ TYPE_PROC_MAP = {} # :nodoc:
1324
+
1325
+ # SQLT_CHR
1326
+ DATA_TYPE_MAP[1] = :varchar2
1327
+ TYPE_PROC_MAP[1] = Proc.new do |p|
1328
+ if p.charset_form == :nchar
1329
+ "NVARCHAR2(#{p.char_size})"
1330
+ else
1331
+ if (p.respond_to? :char_used?) && (p.char_used?)
1332
+ "VARCHAR2(#{p.char_size} CHAR)"
1333
+ else
1334
+ "VARCHAR2(#{p.data_size})"
1335
+ end
1336
+ end
1337
+ end
1338
+
1339
+ # SQLT_NUM
1340
+ DATA_TYPE_MAP[2] = :number
1341
+ TYPE_PROC_MAP[2] = Proc.new do |p|
1342
+ begin
1343
+ case p.scale
1344
+ when -127
1345
+ case p.precision
1346
+ when 0
1347
+ "NUMBER"
1348
+ when 126
1349
+ "FLOAT"
1350
+ else
1351
+ "FLOAT(#{p.precision})"
1352
+ end
1353
+ when 0
1354
+ case p.precision
1355
+ when 0
1356
+ "NUMBER"
1357
+ else
1358
+ "NUMBER(#{p.precision})"
1359
+ end
1360
+ else
1361
+ "NUMBER(#{p.precision},#{p.scale})"
1362
+ end
1363
+ rescue OCIError
1364
+ "NUMBER"
1365
+ end
1366
+ end
1367
+
1368
+ # SQLT_LNG
1369
+ DATA_TYPE_MAP[8] = :long
1370
+ TYPE_PROC_MAP[8] = "LONG"
1371
+
1372
+ # SQLT_DAT
1373
+ DATA_TYPE_MAP[12] = :date
1374
+ TYPE_PROC_MAP[12] = "DATE"
1375
+
1376
+ # SQLT_BIN
1377
+ DATA_TYPE_MAP[23] = :raw
1378
+ TYPE_PROC_MAP[23] = Proc.new do |p|
1379
+ "RAW(#{p.data_size})"
1380
+ end
1381
+
1382
+ # SQLT_LBI
1383
+ DATA_TYPE_MAP[24] = :long_raw
1384
+ TYPE_PROC_MAP[24] = "LONG RAW"
1385
+
1386
+ # SQLT_AFC
1387
+ DATA_TYPE_MAP[96] = :char
1388
+ TYPE_PROC_MAP[96] = Proc.new do |p|
1389
+ if p.charset_form == :nchar
1390
+ "NCHAR(#{p.char_size})"
1391
+ else
1392
+ if (p.respond_to? :char_used?) && (p.char_used?)
1393
+ "CHAR(#{p.char_size} CHAR)"
1394
+ else
1395
+ "CHAR(#{p.data_size})"
1396
+ end
1397
+ end
1398
+ end
1399
+
1400
+ # SQLT_IBFLOAT
1401
+ DATA_TYPE_MAP[100] = :binary_float
1402
+ TYPE_PROC_MAP[100] = "BINARY_FLOAT"
1403
+
1404
+ # SQLT_IBDOUBLE
1405
+ DATA_TYPE_MAP[101] = :binary_double
1406
+ TYPE_PROC_MAP[101] = "BINARY_DOUBLE"
1407
+
1408
+ # SQLT_RDD
1409
+ DATA_TYPE_MAP[104] = :rowid
1410
+ TYPE_PROC_MAP[104] = "ROWID"
1411
+
1412
+ # SQLT_NTY
1413
+ DATA_TYPE_MAP[108] = :named_type
1414
+ TYPE_PROC_MAP[108] = "Object"
1415
+
1416
+ # SQLT_REF
1417
+ DATA_TYPE_MAP[110] = :ref
1418
+ TYPE_PROC_MAP[110] = "REF"
1419
+
1420
+ # SQLT_CLOB
1421
+ DATA_TYPE_MAP[112] = :clob
1422
+ TYPE_PROC_MAP[112] = Proc.new do |p|
1423
+ if p.charset_form == :nchar
1424
+ "NCLOB"
1425
+ else
1426
+ "CLOB"
1427
+ end
1428
+ end
1429
+
1430
+ # SQLT_BLOB
1431
+ DATA_TYPE_MAP[113] = :blob
1432
+ TYPE_PROC_MAP[113] = "BLOB"
1433
+
1434
+ # SQLT_BFILE
1435
+ DATA_TYPE_MAP[114] = :bfile
1436
+ TYPE_PROC_MAP[114] = "BFILE"
1437
+
1438
+ # SQLT_TIMESTAMP
1439
+ DATA_TYPE_MAP[187] = :timestamp
1440
+ TYPE_PROC_MAP[187] = Proc.new do |p|
1441
+ fsprecision = p.fsprecision
1442
+ if fsprecision == 6
1443
+ "TIMESTAMP"
1444
+ else
1445
+ "TIMESTAMP(#{fsprecision})"
1446
+ end
1447
+ end
1448
+
1449
+ # SQLT_TIMESTAMP_TZ
1450
+ DATA_TYPE_MAP[188] = :timestamp_tz
1451
+ TYPE_PROC_MAP[188] = Proc.new do |p|
1452
+ fsprecision = p.fsprecision
1453
+ if fsprecision == 6
1454
+ "TIMESTAMP WITH TIME ZONE"
1455
+ else
1456
+ "TIMESTAMP(#{fsprecision}) WITH TIME ZONE"
1457
+ end
1458
+ end
1459
+
1460
+ # SQLT_INTERVAL_YM
1461
+ DATA_TYPE_MAP[189] = :interval_ym
1462
+ TYPE_PROC_MAP[189] = Proc.new do |p|
1463
+ lfprecision = p.lfprecision
1464
+ if lfprecision == 2
1465
+ "INTERVAL YEAR TO MONTH"
1466
+ else
1467
+ "INTERVAL YEAR(#{lfprecision}) TO MONTH"
1468
+ end
1469
+ end
1470
+
1471
+ # SQLT_INTERVAL_DS
1472
+ DATA_TYPE_MAP[190] = :interval_ds
1473
+ TYPE_PROC_MAP[190] = Proc.new do |p|
1474
+ lfprecision = p.lfprecision
1475
+ fsprecision = p.fsprecision
1476
+ if lfprecision == 2 && fsprecision == 6
1477
+ "INTERVAL DAY TO SECOND"
1478
+ else
1479
+ "INTERVAL DAY(#{lfprecision}) TO SECOND(#{fsprecision})"
1480
+ end
1481
+ end
1482
+
1483
+ # SQLT_TIMESTAMP_LTZ
1484
+ DATA_TYPE_MAP[232] = :timestamp_ltz
1485
+ TYPE_PROC_MAP[232] = Proc.new do |p|
1486
+ fsprecision = p.fsprecision
1487
+ if fsprecision == 6
1488
+ "TIMESTAMP WITH LOCAL TIME ZONE"
1489
+ else
1490
+ "TIMESTAMP(#{fsprecision}) WITH LOCAL TIME ZONE"
1491
+ end
1492
+ end
1493
+
1494
+ def inspect # :nodoc:
1495
+ "#<#{self.class.name}:(#{@obj_id}) #{@obj_schema}.#{@obj_name}>"
1496
+ end
1497
+
1498
+ private
1499
+
1500
+ def __data_type(p)
1501
+ DATA_TYPE_MAP[p.attrGet(OCI_ATTR_DATA_TYPE)] || p.attrGet(OCI_ATTR_DATA_TYPE)
1502
+ end
1503
+
1504
+ def __type_string(p)
1505
+ type = TYPE_PROC_MAP[p.attrGet(OCI_ATTR_DATA_TYPE)] || "unknown(#{p.attrGet(OCI_ATTR_DATA_TYPE)})"
1506
+ type = type.call(self) if type.is_a? Proc
1507
+ if respond_to?(:nullable?) && !nullable?
1508
+ type + " NOT NULL"
1509
+ else
1510
+ type
1511
+ end
1512
+ end
1513
+
1514
+ def initialize_table_or_view(param)
1515
+ @num_cols = param.attrGet(OCI_ATTR_NUM_COLS)
1516
+ @obj_id = param.attrGet(OCI_ATTR_OBJ_ID)
1517
+ @obj_name = param.attrGet(OCI_ATTR_OBJ_NAME)
1518
+ @obj_schema = param.attrGet(OCI_ATTR_OBJ_SCHEMA)
1519
+ colparam = param.attrGet(OCI_ATTR_LIST_COLUMNS)
1520
+ @columns = []
1521
+ 1.upto @num_cols do |i|
1522
+ @columns << OCI8::Metadata::Column.new(colparam.paramGet(i))
1523
+ end
1524
+ end
1525
+ end
1526
+
1527
+ class Table < Base
1528
+ attr_reader :num_cols
1529
+ attr_reader :obj_name
1530
+ attr_reader :obj_schema
1531
+ attr_reader :columns
1532
+
1533
+ def initialize(param)
1534
+ initialize_table_or_view(param)
1535
+ end
1536
+ end
1537
+
1538
+ class View < Base
1539
+ attr_reader :num_cols
1540
+ attr_reader :obj_name
1541
+ attr_reader :obj_schema
1542
+ attr_reader :columns
1543
+
1544
+ def initialize(param)
1545
+ initialize_table_or_view(param)
1546
+ end
1547
+ end
1548
+
1549
+ class Column < Base
1550
+ attr_reader :name
1551
+ attr_reader :type_string
1552
+ attr_reader :data_type
1553
+ attr_reader :charset_form
1554
+ def nullable?; @nullable; end
1555
+
1556
+ # string data type
1557
+ def char_used?; @char_used; end if defined? OCI_ATTR_CHAR_USED
1558
+ attr_reader :char_size if defined? OCI_ATTR_CHAR_SIZE
1559
+ attr_reader :data_size
1560
+ attr_reader :charset_id
1561
+
1562
+ # number data type
1563
+ attr_reader :precision
1564
+ attr_reader :scale
1565
+
1566
+ # interval
1567
+ attr_reader :fsprecision
1568
+ attr_reader :lfprecision
1569
+
1570
+ def initialize(param)
1571
+ @name = param.attrGet(OCI_ATTR_NAME)
1572
+ @data_type = __data_type(param)
1573
+
1574
+ @data_size = param.attrGet(OCI_ATTR_DATA_SIZE)
1575
+ @char_used = param.attrGet(OCI_ATTR_CHAR_USED) if defined? OCI_ATTR_CHAR_USED
1576
+ @char_size = param.attrGet(OCI_ATTR_CHAR_SIZE) if defined? OCI_ATTR_CHAR_SIZE
1577
+
1578
+ @precision = param.attrGet(OCI_ATTR_PRECISION)
1579
+ @scale = param.attrGet(OCI_ATTR_SCALE)
1580
+ @nullable = param.attrGet(OCI_ATTR_IS_NULL)
1581
+ @charset_id = param.attrGet(OCI_ATTR_CHARSET_ID)
1582
+ @charset_form = case param.attrGet(OCI_ATTR_CHARSET_FORM)
1583
+ when 0: nil
1584
+ when 1; :implicit
1585
+ when 2; :nchar
1586
+ when 3; :explicit
1587
+ when 4; :flexible
1588
+ when 5; :lit_null
1589
+ else raise "unknown charset_form #{param.attrGet(OCI_ATTR_CHARSET_FORM)}"
1590
+ end
1591
+ @fsprecision = param.attrGet(OCI_ATTR_FSPRECISION)
1592
+ @lfprecision = param.attrGet(OCI_ATTR_LFPRECISION)
1593
+ @type_string = __type_string(param)
1594
+ end
1595
+
1596
+ def to_s
1597
+ %Q{"#{@name}" #{@type_string}}
1598
+ end
1599
+
1600
+ def inspect # :nodoc:
1601
+ "#<#{self.class.name}: #{@name} #{@type_string}>"
1602
+ end
1603
+ end
1604
+ end
1605
+ end