ruby-oci8 1.0.2-i386-mswin32

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