ruby-oci8-master 2.0.7

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 (84) hide show
  1. data/ChangeLog +2321 -0
  2. data/Makefile +88 -0
  3. data/NEWS +303 -0
  4. data/README +76 -0
  5. data/VERSION +1 -0
  6. data/dist-files +83 -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/.document +18 -0
  13. data/ext/oci8/MANIFEST +18 -0
  14. data/ext/oci8/apiwrap.c.tmpl +182 -0
  15. data/ext/oci8/apiwrap.h.tmpl +61 -0
  16. data/ext/oci8/apiwrap.rb +91 -0
  17. data/ext/oci8/apiwrap.yml +1455 -0
  18. data/ext/oci8/attr.c +105 -0
  19. data/ext/oci8/bind.c +366 -0
  20. data/ext/oci8/connection_pool.c +199 -0
  21. data/ext/oci8/encoding.c +289 -0
  22. data/ext/oci8/env.c +178 -0
  23. data/ext/oci8/error.c +378 -0
  24. data/ext/oci8/extconf.rb +179 -0
  25. data/ext/oci8/lob.c +805 -0
  26. data/ext/oci8/metadata.c +232 -0
  27. data/ext/oci8/object.c +727 -0
  28. data/ext/oci8/oci8.c +1156 -0
  29. data/ext/oci8/oci8.h +574 -0
  30. data/ext/oci8/oci8lib.c +527 -0
  31. data/ext/oci8/ocidatetime.c +484 -0
  32. data/ext/oci8/ocihandle.c +751 -0
  33. data/ext/oci8/ocinumber.c +1612 -0
  34. data/ext/oci8/oraconf.rb +1119 -0
  35. data/ext/oci8/oradate.c +611 -0
  36. data/ext/oci8/oranumber_util.c +352 -0
  37. data/ext/oci8/oranumber_util.h +24 -0
  38. data/ext/oci8/post-config.rb +5 -0
  39. data/ext/oci8/stmt.c +673 -0
  40. data/ext/oci8/thread_util.c +85 -0
  41. data/ext/oci8/thread_util.h +30 -0
  42. data/ext/oci8/win32.c +137 -0
  43. data/lib/.document +1 -0
  44. data/lib/dbd/OCI8.rb +591 -0
  45. data/lib/oci8.rb.in +94 -0
  46. data/lib/oci8/.document +8 -0
  47. data/lib/oci8/bindtype.rb +349 -0
  48. data/lib/oci8/compat.rb +113 -0
  49. data/lib/oci8/connection_pool.rb +99 -0
  50. data/lib/oci8/datetime.rb +611 -0
  51. data/lib/oci8/encoding-init.rb +74 -0
  52. data/lib/oci8/encoding.yml +537 -0
  53. data/lib/oci8/metadata.rb +2132 -0
  54. data/lib/oci8/object.rb +581 -0
  55. data/lib/oci8/oci8.rb +721 -0
  56. data/lib/oci8/ocihandle.rb +425 -0
  57. data/lib/oci8/oracle_version.rb +144 -0
  58. data/lib/oci8/properties.rb +73 -0
  59. data/metaconfig +142 -0
  60. data/pre-distclean.rb +7 -0
  61. data/ruby-oci8.gemspec +63 -0
  62. data/setup.rb +1331 -0
  63. data/test/README +4 -0
  64. data/test/config.rb +122 -0
  65. data/test/test_all.rb +51 -0
  66. data/test/test_appinfo.rb +63 -0
  67. data/test/test_array_dml.rb +333 -0
  68. data/test/test_bind_raw.rb +46 -0
  69. data/test/test_bind_time.rb +178 -0
  70. data/test/test_break.rb +96 -0
  71. data/test/test_clob.rb +82 -0
  72. data/test/test_connstr.rb +81 -0
  73. data/test/test_datetime.rb +582 -0
  74. data/test/test_dbi.rb +366 -0
  75. data/test/test_dbi_clob.rb +53 -0
  76. data/test/test_encoding.rb +100 -0
  77. data/test/test_error.rb +88 -0
  78. data/test/test_metadata.rb +1399 -0
  79. data/test/test_oci8.rb +434 -0
  80. data/test/test_oracle_version.rb +70 -0
  81. data/test/test_oradate.rb +256 -0
  82. data/test/test_oranumber.rb +746 -0
  83. data/test/test_rowid.rb +33 -0
  84. metadata +137 -0
@@ -0,0 +1,1119 @@
1
+ require 'mkmf'
2
+
3
+ # compatibility for ruby-1.9
4
+ RbConfig = Config unless defined? RbConfig
5
+
6
+ module MiniRegistry
7
+ class MiniRegistryError < StandardError
8
+ attr_reader :api_name
9
+ attr_reader :code
10
+ def initialize(api_name, code)
11
+ @api_name = api_name
12
+ @code = code
13
+ end
14
+ end
15
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
16
+ # Windows
17
+ require 'Win32API' # raise LoadError when UNIX.
18
+
19
+ # I looked in Win32Module by MoonWolf <URL:http://www.moonwolf.com/ruby/>,
20
+ # copy the minimum code and reorganize it.
21
+ ERROR_SUCCESS = 0
22
+ ERROR_FILE_NOT_FOUND = 2
23
+ ERROR_NO_MORE_ITEMS = 259
24
+
25
+ HKEY_LOCAL_MACHINE = 0x80000002
26
+ KEY_ENUMERATE_SUB_KEYS = 0x0008
27
+ KEY_QUERY_VALUE = 0x0001
28
+ RegOpenKeyExA = Win32API.new('advapi32', 'RegOpenKeyExA', 'LPLLP', 'L')
29
+ RegEnumKeyExA = Win32API.new('advapi32', 'RegEnumKeyExA', 'LLPPPPPP', 'L')
30
+ RegQueryValueExA = Win32API.new('advapi32','RegQueryValueExA','LPPPPP','L')
31
+ RegCloseKey = Win32API.new('advapi32', 'RegCloseKey', 'L', 'L')
32
+
33
+ def self.get_str_value(hKey, name)
34
+ lpcbData = [0].pack('L')
35
+ code = RegQueryValueExA.call(hKey, name, nil, nil, nil, lpcbData)
36
+ if code == ERROR_FILE_NOT_FOUND
37
+ return nil
38
+ elsif code != ERROR_SUCCESS
39
+ raise MiniRegistryError.new("Win32::RegQueryValueExA",code)
40
+ end
41
+ len = lpcbData.unpack('L')[0]
42
+ lpType = [0].pack('L')
43
+ lpData = "\0"*len
44
+ lpcbData = [len].pack('L')
45
+ code = RegQueryValueExA.call(hKey, name, nil, lpType, lpData, lpcbData)
46
+ if code != ERROR_SUCCESS
47
+ raise MiniRegistryError.new("Win32::RegQueryValueExA",code)
48
+ end
49
+ lpData.unpack('Z*')[0]
50
+ end
51
+
52
+ def self.enum_homes
53
+ phkResult = [0].pack('L')
54
+ code = RegOpenKeyExA.call(HKEY_LOCAL_MACHINE, 'SOFTWARE\ORACLE', 0, 0x20019, phkResult)
55
+ if code != ERROR_SUCCESS
56
+ raise MiniRegistryError.new("Win32::RegOpenKeyExA", code)
57
+ end
58
+ hKey = phkResult.unpack('L')[0]
59
+ idx = 0
60
+ maxkeylen = 256
61
+ loop do
62
+ lpName = "\0" * maxkeylen
63
+ lpcName = [maxkeylen].pack('L')
64
+ code = RegEnumKeyExA.call(hKey, idx, lpName, lpcName, nil, nil, nil, nil);
65
+ break if code == ERROR_NO_MORE_ITEMS
66
+ if code != ERROR_SUCCESS
67
+ RegCloseKey.call(hKey)
68
+ raise MiniRegistryError.new("Win32::RegEnumKeyEx", code)
69
+ end
70
+ code = RegOpenKeyExA.call(hKey, lpName, 0, KEY_QUERY_VALUE, phkResult)
71
+ if code != ERROR_SUCCESS
72
+ RegCloseKey.call(hKey)
73
+ raise MiniRegistryError.new("Win32::RegEnumKeyEx", code)
74
+ end
75
+ hSubkey = phkResult.unpack('L')[0]
76
+
77
+ name = get_str_value(hSubkey, 'ORACLE_HOME_NAME')
78
+ path = get_str_value(hSubkey, 'ORACLE_HOME')
79
+ yield name, path
80
+ RegCloseKey.call(hSubkey)
81
+ idx += 1
82
+ end
83
+ RegCloseKey.call(hKey)
84
+ end
85
+
86
+ end
87
+ end # module MiniRegistry
88
+
89
+ # minimal implementation to read information of a shared object.
90
+ class MiniSOReader
91
+ attr_reader :file_format
92
+ attr_reader :cpu
93
+ attr_reader :endian
94
+ attr_reader :bits
95
+
96
+ def initialize(filename)
97
+ f = open(filename, 'rb')
98
+ begin
99
+ case file_header = f.read(2)
100
+ when "\177E"
101
+ # Linux, Solaris and HP-UX(64-bit)
102
+ read_elf(f) if f.read(2) == 'LF'
103
+ when "MZ"
104
+ # Windows
105
+ read_pe(f)
106
+ when "\x02\x10"
107
+ # HP-UX PA-RISC1.1
108
+ read_parisc(f)
109
+ when "\xfe\xed"
110
+ # Big-endian Mach-O File
111
+ read_mach_o_be(f)
112
+ when "\xce\xfa"
113
+ # 32-bit Little-endian Mach-O File
114
+ read_mach_o_le(f, 32)
115
+ when "\xcf\xfa"
116
+ # 64-bit Little-endian Mach-O File
117
+ read_mach_o_le(f, 64)
118
+ when "\xca\xfe"
119
+ # Universal binary
120
+ read_mach_o_unversal(f)
121
+ else
122
+ # AIX and Tru64
123
+ raise format("unknown file header: %02x %02x", file_header[0].to_i, file_header[1].to_i)
124
+ end
125
+ ensure
126
+ f.close
127
+ end
128
+ end
129
+
130
+ private
131
+ # ELF format
132
+ def read_elf(f)
133
+ # 0-3 "\177ELF"
134
+ @file_format = :elf
135
+ # 4
136
+ case f.read(1).unpack('C')[0]
137
+ when 1
138
+ @bits = 32
139
+ when 2
140
+ @bits = 64
141
+ else
142
+ raise 'Invalid ELF class'
143
+ end
144
+ # 5
145
+ case f.read(1).unpack('C')[0]
146
+ when 1
147
+ @endian = :little
148
+ pack_type_short = 'v'
149
+ when 2
150
+ @endian = :big
151
+ pack_type_short = 'n'
152
+ else
153
+ raise 'Invalid ELF byte order'
154
+ end
155
+ # 6
156
+ raise 'Invalid ELF header version' if f.read(1) != "\x01"
157
+ # 16-17
158
+ f.seek(16, IO::SEEK_SET)
159
+ raise 'Invalid ELF filetype' if f.read(2).unpack(pack_type_short)[0] != 3
160
+ # 18-19
161
+ case archtype = f.read(2).unpack(pack_type_short)[0]
162
+ when 2
163
+ @cpu = :sparc
164
+ when 3
165
+ @cpu = :i386
166
+ when 15
167
+ @cpu = :parisc
168
+ when 18
169
+ @cpu = :sparc32plus
170
+ when 20
171
+ @cpu = :ppc
172
+ when 21
173
+ @cpu = :ppc64
174
+ when 22
175
+ @cpu = :s390
176
+ when 43
177
+ @cpu = :sparcv9
178
+ when 50
179
+ @cpu = :ia64
180
+ when 62
181
+ @cpu = :x86_64
182
+ else
183
+ raise "Invalid ELF archtype: #{archtype}"
184
+ end
185
+ end
186
+
187
+ # PE/COFF format
188
+ def read_pe(f)
189
+ # 0-1 "MZ"
190
+ @file_format = :pe
191
+ # 60-63
192
+ f.seek(60, IO::SEEK_SET)
193
+ pe_offset = f.read(4).unpack('V')[0]
194
+ # read PE signature
195
+ f.seek(pe_offset)
196
+ raise 'invalid pe format' if f.read(4) != "PE\000\000"
197
+ # read COFF header
198
+ case machine = f.read(2).unpack('v')[0]
199
+ when 0x014c
200
+ @cpu = :i386
201
+ @endian = :little
202
+ @bits = 32
203
+ when 0x0200
204
+ @cpu = :ia64
205
+ @endian = :little
206
+ @bits = 64
207
+ when 0x8664
208
+ @cpu = :x86_64
209
+ @endian = :little
210
+ @bits = 64
211
+ else
212
+ raise "Invalid coff machine: #{machine}"
213
+ end
214
+ end
215
+
216
+ # HP-UX PA-RISC(32 bit)
217
+ def read_parisc(f)
218
+ # 0-1 system_id - CPU_PA_RISC1_1
219
+ @file_format = :pa_risc
220
+ # 2-3 a_magic - SHL_MAGIC
221
+ raise 'invalid a_magic' if f.read(2).unpack('n')[0] != 0x10e
222
+ @bits = 32
223
+ @endian = :big
224
+ @cpu = :parisc
225
+ end
226
+
227
+ # Big-endian Mach-O File
228
+ def read_mach_o_be(f)
229
+ @file_format = :mach_o
230
+ @endian = :big
231
+ case f.read(2)
232
+ when "\xfa\xce" # feedface
233
+ @cpu = :ppc
234
+ @bits = 32
235
+ when "\xfa\xcf" # feedfacf
236
+ @cpu = :ppc64
237
+ @bits = 64
238
+ else
239
+ raise "unknown file format"
240
+ end
241
+ end
242
+
243
+ def read_mach_o_le(f, bits)
244
+ @file_format = :mach_o
245
+ @endian = :little
246
+ raise 'unknown file format' if f.read(2) != "\xed\xfe"
247
+ case bits
248
+ when 32
249
+ @cpu = :i386
250
+ @bits = 32
251
+ when 64
252
+ @cpu = :x86_64
253
+ @bits = 64
254
+ end
255
+ end
256
+
257
+ def read_mach_o_unversal(f)
258
+ raise 'unknown file format' if f.read(2) != "\xba\xbe" # cafebabe
259
+ @file_format = :universal
260
+ nfat_arch = f.read(4).unpack('N')[0]
261
+ @cpu = []
262
+ @endian = []
263
+ @bits = []
264
+ nfat_arch.times do
265
+ case cputype = f.read(4).unpack('N')[0]
266
+ when 7
267
+ @cpu << :i386
268
+ @endian << :little
269
+ @bits << 32
270
+ when 7 + 0x01000000
271
+ @cpu << :x86_64
272
+ @endian << :little
273
+ @bits << 64
274
+ when 18
275
+ @cpu << :ppc
276
+ @endian << :big
277
+ @bits << 32
278
+ when 18 + 0x01000000
279
+ @cpu << :ppc64
280
+ @endian << :big
281
+ @bits << 64
282
+ else
283
+ raise "Unknown mach-o cputype: #{cputype}"
284
+ end
285
+ f.seek(4 * 4, IO::SEEK_CUR)
286
+ end
287
+ end
288
+ end
289
+
290
+ class OraConf
291
+ attr_reader :cc_is_gcc
292
+ attr_reader :version
293
+ attr_reader :cflags
294
+ attr_reader :libs
295
+
296
+ def initialize
297
+ raise 'use OraConf.get instead'
298
+ end
299
+
300
+ def self.get
301
+ original_CFLAGS = $CFLAGS
302
+ original_defs = $defs
303
+ ic_dir = nil
304
+ begin
305
+ # check Oracle instant client
306
+ if with_config('instant-client')
307
+ puts <<EOS
308
+ =======================================================
309
+
310
+ '--with-instant-client' is an obsolete option. ignore it.
311
+
312
+ =======================================================
313
+ EOS
314
+ end
315
+ ic_dir = check_ic_dir
316
+ if ic_dir
317
+ OraConfIC.new(ic_dir)
318
+ else
319
+ OraConfFC.new()
320
+ end
321
+ rescue
322
+ case ENV['LANG']
323
+ when /^ja/
324
+ lang = 'ja'
325
+ else
326
+ lang = 'en'
327
+ end
328
+ print <<EOS
329
+ ---------------------------------------------------
330
+ Error Message:
331
+ #{$!.to_s.gsub(/\n/, "\n ")}
332
+ Backtrace:
333
+ #{$!.backtrace.join("\n ")}
334
+ ---------------------------------------------------
335
+ See:
336
+ * http://ruby-oci8.rubyforge.org/#{lang}/HowToInstall.html
337
+ * http://ruby-oci8.rubyforge.org/#{lang}/ReportInstallProblem.html
338
+
339
+ EOS
340
+ exc = RuntimeError.new
341
+ exc.set_backtrace($!.backtrace)
342
+ raise exc
343
+ ensure
344
+ $CFLAGS = original_CFLAGS
345
+ $defs = original_defs
346
+ end
347
+ end
348
+
349
+ def self.ld_envs
350
+ @@ld_envs
351
+ end
352
+
353
+ private
354
+
355
+ def self.make_proc_to_check_cpu(*expect)
356
+ Proc.new do |file|
357
+ so = MiniSOReader.new(file)
358
+ if expect.include? so.cpu
359
+ true
360
+ else
361
+ puts " skip: #{file} is for #{so.cpu} cpu."
362
+ false
363
+ end
364
+ end
365
+ end
366
+
367
+ def self.check_ic_dir
368
+ puts "checking for load library path... "
369
+ STDOUT.flush
370
+
371
+ # get library load path names
372
+ oci_basename = 'libclntsh'
373
+ oci_glob_postfix = '.[0-9]*'
374
+ nls_data_basename = ['libociei', 'libociicus']
375
+ @@ld_envs = %w[LD_LIBRARY_PATH]
376
+ so_ext = 'so'
377
+ nls_data_ext = nil
378
+ check_proc = nil
379
+ size_of_pointer = begin
380
+ # the size of a pointer.
381
+ [nil].pack('P').size
382
+ rescue ArgumentError
383
+ # Rubinius 1.2.3 doesn't support Array#pack('P').
384
+ # Use Fixnum#size, which returns the size of long.
385
+ 1.size
386
+ end
387
+ is_32bit = size_of_pointer == 4
388
+ is_big_endian = "\x01\x02".unpack('s')[0] == 0x0102
389
+ case RUBY_PLATFORM
390
+ when /mswin32|cygwin|mingw32|bccwin32/
391
+ oci_basename = 'oci'
392
+ oci_glob_postfix = ''
393
+ nls_data_basename = ['oraociei11', 'oraociicus11', 'oraociei10', 'oraociicus10']
394
+ @@ld_envs = %w[PATH]
395
+ so_ext = 'dll'
396
+ when /i.86-linux/
397
+ check_proc = make_proc_to_check_cpu(:i386)
398
+ when /ia64-linux/
399
+ check_proc = make_proc_to_check_cpu(:ia64)
400
+ when /x86_64-linux/
401
+ # RUBY_PLATFORM depends on the compilation environment.
402
+ # Even though it is x86_64-linux, the compiled ruby may
403
+ # be a 32-bit executable.
404
+ check_proc = make_proc_to_check_cpu(is_32bit ? :i386 : :x86_64)
405
+ when /solaris/
406
+ if is_32bit
407
+ @@ld_envs = %w[LD_LIBRARY_PATH_32 LD_LIBRARY_PATH]
408
+ if is_big_endian
409
+ check_proc = make_proc_to_check_cpu(:sparc, :sparc32plus)
410
+ else
411
+ check_proc = make_proc_to_check_cpu(:i386)
412
+ end
413
+ else
414
+ @@ld_envs = %w[LD_LIBRARY_PATH_64 LD_LIBRARY_PATH]
415
+ if is_big_endian
416
+ check_proc = make_proc_to_check_cpu(:sparcv9)
417
+ else
418
+ check_proc = make_proc_to_check_cpu(:x86_64)
419
+ end
420
+ end
421
+ when /aix/
422
+ oci_glob_postfix = ''
423
+ @@ld_envs = %w[LIBPATH]
424
+ so_ext = 'a'
425
+ nls_data_ext = 'so'
426
+ when /hppa.*-hpux/
427
+ if is_32bit
428
+ @@ld_envs = %w[SHLIB_PATH]
429
+ end
430
+ so_ext = 'sl'
431
+ when /darwin/
432
+ @@ld_envs = %w[DYLD_LIBRARY_PATH]
433
+ so_ext = 'dylib'
434
+ if is_32bit
435
+ if is_big_endian
436
+ this_cpu = :ppc # 32-bit big-endian
437
+ else
438
+ this_cpu = :i386 # 32-bit little-endian
439
+ end
440
+ else
441
+ if is_big_endian
442
+ this_cpu = :ppc64 # 64-bit big-endian
443
+ else
444
+ this_cpu = :x86_64 # 64-bit little-endian
445
+ end
446
+ end
447
+ check_proc = Proc.new do |file|
448
+ so = MiniSOReader.new(file)
449
+ if so.file_format == :universal
450
+ if so.cpu.include? this_cpu
451
+ true
452
+ else
453
+ if so.cpu.size > 1
454
+ arch_types = so.cpu[0..-2].join(', ') + ' and ' + so.cpu[-1].to_s
455
+ else
456
+ arch_types = so.cpu[0]
457
+ end
458
+ puts " skip: #{file} is for #{arch_types} cpu."
459
+ false
460
+ end
461
+ else
462
+ if so.cpu == this_cpu
463
+ true
464
+ else
465
+ puts " skip: #{file} is for #{so.cpu} cpu."
466
+ false
467
+ end
468
+ end
469
+ end
470
+ end
471
+
472
+ glob_name = "#{oci_basename}.#{so_ext}#{oci_glob_postfix}"
473
+ ld_path = nil
474
+ file = nil
475
+ @@ld_envs.each do |env|
476
+ if ENV[env].nil?
477
+ puts " #{env} is not set."
478
+ next
479
+ end
480
+ puts " #{env}... "
481
+ ld_path, file = check_lib_in_path(ENV[env], glob_name, check_proc)
482
+ break if ld_path
483
+ end
484
+
485
+ if ld_path.nil?
486
+ case RUBY_PLATFORM
487
+ when /linux/
488
+ open("|/sbin/ldconfig -p") do |f|
489
+ print " checking ld.so.conf... "
490
+ STDOUT.flush
491
+ while line = f.gets
492
+ if line =~ /libclntsh\.so\..* => (\/.*)\/libclntsh\.so\.(.*)/
493
+ file = "#$1/libclntsh.so.#$2"
494
+ path = $1
495
+ next if (check_proc && !check_proc.call(file))
496
+ ld_path = path
497
+ puts "yes"
498
+ break
499
+ end
500
+ end
501
+ puts "no"
502
+ end
503
+ when /solaris/
504
+ if is_32bit
505
+ crle_cmd = 'crle'
506
+ else
507
+ crle_cmd = 'crle -64'
508
+ end
509
+ open('|env LANG=C /usr/bin/' + crle_cmd) do |f|
510
+ while line = f.gets
511
+ if line =~ /Default Library Path[^:]*:\s*(\S*)/
512
+ puts " checking output of `#{crle_cmd}'... "
513
+ ld_path, file = check_lib_in_path($1, glob_name, check_proc)
514
+ break
515
+ end
516
+ end
517
+ end
518
+ end
519
+ end
520
+
521
+ if ld_path
522
+ nls_data_ext ||= so_ext # nls_data_ext is same with so_ext by default.
523
+ nls_data_basename.each do |basename|
524
+ if File.exist?(File.join(ld_path, "#{basename}.#{nls_data_ext}"))
525
+ puts " #{file} looks like an instant client."
526
+ return ld_path
527
+ end
528
+ end
529
+ puts " #{file} looks like a full client."
530
+ end
531
+ nil
532
+ end
533
+
534
+ def self.check_lib_in_path(paths, glob_name, check_proc)
535
+ paths.split(File::PATH_SEPARATOR).each do |path|
536
+ next if path.nil? or path == ''
537
+ print " checking #{path}... "
538
+ path.gsub!(/\\/, '/') if /mswin32|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
539
+ files = Dir.glob(File.join(path, glob_name))
540
+ if files.empty?
541
+ puts "no"
542
+ next
543
+ end
544
+ STDOUT.flush
545
+ next if (check_proc && !check_proc.call(files[0]))
546
+ puts "yes"
547
+ return path, files[0]
548
+ end
549
+ nil
550
+ end
551
+
552
+ def init
553
+ check_cc()
554
+ @cc_is_gcc = check_cc_is_gcc()
555
+ @lp64 = check_lp64()
556
+ check_system_header()
557
+ check_ruby_header()
558
+ end
559
+
560
+ def check_cc
561
+ print "checking for cc... "
562
+ STDOUT.flush
563
+ if try_run("int main() { return 0; }")
564
+ puts "ok"
565
+ else
566
+ puts "ng"
567
+ raise "C compiler doesn't work correctly."
568
+ end
569
+ end # check_cc
570
+
571
+ def check_cc_is_gcc
572
+ # bcc defines __GNUC__. why??
573
+ return false if RUBY_PLATFORM =~ /bccwin32/
574
+
575
+ print "checking for gcc... "
576
+ STDOUT.flush
577
+ if macro_defined?("__GNUC__", "")
578
+ print "yes\n"
579
+ return true
580
+ else
581
+ print "no\n"
582
+ return false
583
+ end
584
+ end # check_cc_is_gcc
585
+
586
+ def check_lp64
587
+ print "checking for LP64... "
588
+ STDOUT.flush
589
+ if try_run("int main() { return sizeof(long) == 8 ? 0 : 1; }")
590
+ puts "yes"
591
+ true
592
+ else
593
+ puts "no"
594
+ false
595
+ end
596
+ end # check_lp64
597
+
598
+ def check_system_header
599
+ if not have_header('sys/types.h')
600
+ raise <<EOS
601
+ A standard C header file 'sys/types.h' doesn't exist.
602
+ Did you install glibc-devel(redhat) or libc6-dev(debian/ubuntu)?
603
+ EOS
604
+ end
605
+ end
606
+
607
+ def check_ruby_header
608
+ print "checking for ruby header... "
609
+ STDOUT.flush
610
+ rubyhdrdir = RbConfig::CONFIG["rubyhdrdir"] || RbConfig::CONFIG['archdir']
611
+ unless File.exist?(rubyhdrdir + '/ruby.h')
612
+ puts "ng"
613
+ if RUBY_PLATFORM =~ /darwin/ and File.exist?("#{RbConfig::CONFIG['archdir']}/../universal-darwin8.0/ruby.h")
614
+ raise <<EOS
615
+ #{RbConfig::CONFIG['archdir']}/ruby.h doesn't exist.
616
+ Run the following commands to fix the problem.
617
+
618
+ cd #{RbConfig::CONFIG['archdir']}
619
+ sudo ln -s ../universal-darwin8.0/* ./
620
+ EOS
621
+ else
622
+ raise <<EOS
623
+ #{RbConfig::CONFIG['archdir']}/ruby.h doesn't exist.
624
+ Install the ruby development library.
625
+ EOS
626
+ end
627
+ end
628
+ puts "ok"
629
+ $stdout.flush
630
+ end # check_ruby_header
631
+
632
+ def try_link_oci
633
+ original_libs = $libs
634
+ begin
635
+ $libs += " -L#{CONFIG['libdir']} " + @libs
636
+ have_func("OCIInitialize", "oci.h")
637
+ ensure
638
+ $libs = original_libs
639
+ end
640
+ end
641
+
642
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
643
+
644
+ def get_libs(base_dir = oci_base_dir)
645
+ case RUBY_PLATFORM
646
+ when /cygwin/
647
+ open("OCI.def", "w") do |f|
648
+ f.puts("EXPORTS")
649
+ open("|nm #{base_dir}/LIB/MSVC/OCI.LIB") do |r|
650
+ while line = r.gets
651
+ f.puts($') if line =~ / T _/
652
+ end
653
+ end
654
+ end
655
+ command = "dlltool -d OCI.def -D OCI.DLL -l libOCI.a"
656
+ print("Running: '#{command}' ...")
657
+ STDOUT.flush
658
+ system(command)
659
+ puts("done")
660
+ "-L. -lOCI"
661
+ when /bccwin32/
662
+ # replace '/' to '\\' because bcc linker misunderstands
663
+ # 'C:/foo/bar/OCI.LIB' as unknown option.
664
+ lib = "#{base_dir}/LIB/BORLAND/OCI.LIB"
665
+ return lib.tr('/', '\\') if File.exist?(lib)
666
+ raise <<EOS
667
+ #{lib} does not exist.
668
+
669
+ Your Oracle may not support Borland C++.
670
+ If you want to run this module, run the following command at your own risk.
671
+ cd #{base_dir.tr('/', '\\')}\\LIB
672
+ mkdir Borland
673
+ cd Borland
674
+ coff2omf ..\\MSVC\\OCI.LIB OCI.LIB
675
+ EOS
676
+ exit 1
677
+ else
678
+ "\"#{base_dir}/LIB/MSVC/OCI.LIB\""
679
+ end
680
+ end
681
+
682
+ else
683
+ # Unix
684
+ def get_libs(lib_dir)
685
+ case RUBY_PLATFORM
686
+ when /solaris/
687
+ " -L#{lib_dir} -R#{lib_dir} -lclntsh"
688
+ when /linux/
689
+ " -L#{lib_dir} -Wl,-rpath,#{lib_dir} -lclntsh"
690
+ else
691
+ " -L#{lib_dir} -lclntsh"
692
+ end
693
+ end
694
+
695
+ end
696
+ end
697
+
698
+ # OraConf for Full Client
699
+ class OraConfFC < OraConf
700
+ def initialize
701
+ init
702
+
703
+ @oracle_home = get_home()
704
+ if RUBY_PLATFORM =~ /freebsd/ && @oracle_home == '/usr/local/oracle8-client'
705
+ @version = '817'
706
+ else
707
+ @version = get_version()
708
+ end
709
+ @cflags = get_cflags()
710
+ $CFLAGS += @cflags
711
+
712
+ if !@lp64 && File.exist?("#{@oracle_home}/lib32")
713
+ # ruby - 32bit
714
+ # oracle - 64bit
715
+ use_lib32 = true
716
+ else
717
+ use_lib32 = false
718
+ end
719
+
720
+ if use_lib32
721
+ lib_dir = "#{@oracle_home}/lib32"
722
+ else
723
+ lib_dir = "#{@oracle_home}/lib"
724
+ end
725
+ @libs = get_libs(lib_dir)
726
+ return if try_link_oci()
727
+
728
+ raise 'cannot compile OCI'
729
+ end
730
+
731
+ private
732
+
733
+ def get_version
734
+ print("Get the version of Oracle from SQL*Plus... ")
735
+ STDOUT.flush
736
+ version = nil
737
+ dev_null = RUBY_PLATFORM =~ /mswin32|mingw32|bccwin32/ ? "nul" : "/dev/null"
738
+ if File.exist?("#{@oracle_home}/bin/plus80.exe")
739
+ sqlplus = "plus80.exe"
740
+ else
741
+ sqlplus = "sqlplus"
742
+ end
743
+ Logging::open do
744
+ ENV['NLS_LANG'] = 'american_america.us7ascii'
745
+ open("|#{@oracle_home}/bin/#{sqlplus} < #{dev_null}") do |f|
746
+ while line = f.gets
747
+ print line
748
+ if line =~ /(\d+)\.(\d)\.(\d)/
749
+ version = $1 + $2 + $3
750
+ break
751
+ end
752
+ end
753
+ end
754
+ end
755
+ if version.nil?
756
+ raise 'cannot get Oracle version from sqlplus'
757
+ end
758
+ puts version
759
+ version
760
+ end # get_version
761
+
762
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
763
+
764
+ def is_valid_home?(oracle_home)
765
+ return false if oracle_home.nil?
766
+ sqlplus = "#{oracle_home}/bin/sqlplus.exe"
767
+ print("checking for ORACLE_HOME(#{oracle_home})... ")
768
+ STDOUT.flush
769
+ if File.exist?(sqlplus)
770
+ puts("yes")
771
+ true
772
+ else
773
+ puts("no")
774
+ false
775
+ end
776
+ end
777
+
778
+ def get_home()
779
+ oracle_home = ENV['ORACLE_HOME']
780
+ if oracle_home.nil?
781
+ struct = Struct.new("OracleHome", :name, :path)
782
+ oracle_homes = []
783
+ MiniRegistry.enum_homes do |name, path|
784
+ path.chomp!("\\") if path
785
+ oracle_homes << struct.new(name, path) if is_valid_home?(path)
786
+ end
787
+ if oracle_homes.empty?
788
+ raise <<EOS
789
+ Set the environment variable ORACLE_HOME if Oracle Full Client.
790
+ Append the path of Oracle client libraries to #{OraConf.ld_envs[0]} if Oracle Instant Client.
791
+ EOS
792
+ end
793
+ if oracle_homes.length == 1
794
+ oracle_home = oracle_homes[0].path
795
+ else
796
+ default_path = ''
797
+ if RUBY_PLATFORM =~ /cygwin/
798
+ path_sep = ':'
799
+ dir_sep = '/'
800
+ else
801
+ path_sep = ';'
802
+ dir_sep = '\\'
803
+ end
804
+ ENV['PATH'].split(path_sep).each do |path|
805
+ path.chomp!(dir_sep)
806
+ if File.exist?("#{path}/OCI.DLL")
807
+ default_path = path
808
+ break
809
+ end
810
+ end
811
+ puts "---------------------------------------------------"
812
+ puts "Multiple Oracle Homes are found."
813
+ printf " %-15s : %s\n", "[NAME]", "[PATH]"
814
+ oracle_homes.each do |home|
815
+ if RUBY_PLATFORM =~ /cygwin/
816
+ path = `cygpath -u '#{home.path}'`.chomp!
817
+ else
818
+ path = home.path
819
+ end
820
+ if default_path.downcase == "#{path.downcase}#{dir_sep}bin"
821
+ oracle_home = home
822
+ end
823
+ printf " %-15s : %s\n", home.name, home.path
824
+ end
825
+ if oracle_home.nil?
826
+ puts "default oracle home is not found."
827
+ puts "---------------------------------------------------"
828
+ raise 'Cannot get ORACLE_HOME. Please set the environment valiable ORACLE_HOME.'
829
+ else
830
+ printf "use %s\n", oracle_home.name
831
+ puts "run ohsel.exe to use another oracle home."
832
+ puts "---------------------------------------------------"
833
+ oracle_home = oracle_home.path
834
+ end
835
+ end
836
+ end
837
+ if RUBY_PLATFORM =~ /cygwin/
838
+ oracle_home = oracle_home.sub(/^([a-zA-Z]):/, "/cygdrive/\\1")
839
+ end
840
+ oracle_home.gsub(/\\/, '/')
841
+ end
842
+
843
+ def oci_base_dir
844
+ case @version
845
+ when /80./
846
+ "#{@oracle_home}/OCI80"
847
+ else
848
+ "#{@oracle_home}/OCI"
849
+ end
850
+ end
851
+
852
+ def get_cflags
853
+ unless File.exist?("#{oci_base_dir}/INCLUDE/OCI.H")
854
+ raise "'#{oci_base_dir}/INCLUDE/OCI.H' does not exists. Please install 'Oracle Call Interface'."
855
+ end
856
+ if RUBY_PLATFORM =~ /cygwin/
857
+ " \"-I#{oci_base_dir}/INCLUDE\" -D_int64=\"long long\""
858
+ else
859
+ " \"-I#{oci_base_dir}/INCLUDE\""
860
+ end
861
+ end
862
+
863
+ else # when UNIX
864
+
865
+ def get_home
866
+ oracle_home = ENV['ORACLE_HOME']
867
+ if oracle_home.nil?
868
+ msg = <<EOS
869
+ Set the environment variable ORACLE_HOME if Oracle Full Client.
870
+ Append the path of Oracle client libraries to #{OraConf.ld_envs[0]} if Oracle Instant Client.
871
+ EOS
872
+
873
+ # check sudo environment
874
+ sudo_command = ENV['SUDO_COMMAND']
875
+ if /\w*make\b/ =~ sudo_command
876
+ msg += <<EOS
877
+
878
+ The 'sudo' command unset some environment variables for security reasons.
879
+ Use it only when running 'make install' as follows
880
+ make
881
+ sudo make install
882
+ EOS
883
+ end
884
+ if /\w+\/gem\b/ =~ sudo_command
885
+ msg += <<EOS
886
+
887
+ The 'sudo' command unset some environment variables for security reasons.
888
+ Pass required varialbes as follows
889
+ sudo env #{OraConf.ld_envs[0]}=$#{OraConf.ld_envs[0]} #{sudo_command}
890
+ or
891
+ sudo env ORACLE_HOME=$ORACLE_HOME #{sudo_command}
892
+ EOS
893
+ end
894
+ raise msg
895
+ end
896
+ oracle_home
897
+ end
898
+
899
+ def get_cflags
900
+ cflags = ''
901
+ ok = false
902
+ original_CFLAGS = $CFLAGS
903
+ begin
904
+ for i in ["rdbms/public", "rdbms/demo", "network/public", "plsql/public"]
905
+ cflags += " -I#{@oracle_home}/#{i}"
906
+ $CFLAGS += " -I#{@oracle_home}/#{i}"
907
+ print("try #{cflags}\n");
908
+ if have_header("oci.h")
909
+ ok = true
910
+ break
911
+ end
912
+ end
913
+ unless ok
914
+ if @version.to_i >= 1000
915
+ oci_h = "#{@oracle_home}/rdbms/public/oci.h"
916
+ else
917
+ oci_h = "#{@oracle_home}/rdbms/demo/oci.h"
918
+ end
919
+ unless File.exist?(oci_h)
920
+ raise "'#{oci_h}' does not exists. Install 'Oracle Call Interface' component."
921
+ end
922
+ raise 'Cannot get proper cflags.'
923
+ end
924
+ cflags
925
+ ensure
926
+ $CFLAGS = original_CFLAGS
927
+ end
928
+ end # get_cflags
929
+
930
+ end
931
+ end
932
+
933
+ # OraConf for Instant Client
934
+ class OraConfIC < OraConf
935
+ def initialize(ic_dir)
936
+ init
937
+
938
+ if ic_dir =~ /^\/usr\/lib(?:64)?\/oracle\/(\d+(?:\.\d+)*)\/client(64)?\/lib(?:64)?/
939
+ # rpm package
940
+ # x86 rpms after 11.1.0.7.0:
941
+ # library: /usr/lib/oracle/X.X/client/lib/
942
+ # include: /usr/include/oracle/X.X/client/
943
+ #
944
+ # x86_64 rpms after 11.1.0.7.0:
945
+ # library: /usr/lib/oracle/X.X/client64/lib/
946
+ # include: /usr/include/oracle/X.X/client64/
947
+ #
948
+ # x86 rpms before 11.1.0.6.0:
949
+ # library: /usr/lib/oracle/X.X.X.X/client/lib/
950
+ # include: /usr/include/oracle/X.X.X.X/client/
951
+ #
952
+ # x86_64 rpms before 11.1.0.6.0:
953
+ # library: /usr/lib/oracle/X.X.X.X/client64/lib/
954
+ # include: /usr/include/oracle/X.X.X.X/client64/
955
+ #
956
+ # third-party x86_64 rpms(*1):
957
+ # library: /usr/lib64/oracle/X.X.X.X/client/lib/
958
+ # or /usr/lib64/oracle/X.X.X.X/client/lib64/
959
+ # include: /usr/include/oracle/X.X.X.X/client/
960
+ #
961
+ # *1 These had been used before Oracle released official x86_64 rpms.
962
+ #
963
+ lib_dir = ic_dir
964
+ inc_dir = "/usr/include/oracle/#{$1}/client#{$2}"
965
+ else
966
+ # zip package
967
+ lib_dir = ic_dir
968
+ inc_dir = "#{ic_dir}/sdk/include"
969
+ end
970
+
971
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
972
+ unless File.exist?("#{ic_dir}/sdk/lib/msvc/oci.lib")
973
+ raise <<EOS
974
+ Could not compile with Oracle instant client.
975
+ #{ic_dir}/sdk/lib/msvc/oci.lib could not be found.
976
+ EOS
977
+ raise 'failed'
978
+ end
979
+ @cflags = " \"-I#{inc_dir}\""
980
+ @cflags += " -D_int64=\"long long\"" if RUBY_PLATFORM =~ /cygwin/
981
+ @libs = get_libs("#{ic_dir}/sdk")
982
+ ld_path = nil
983
+ else
984
+ @cflags = " -I#{inc_dir}"
985
+ # set ld_path and so_ext
986
+ case RUBY_PLATFORM
987
+ when /aix/
988
+ ld_path = 'LIBPATH'
989
+ so_ext = 'a'
990
+ when /hppa.*-hpux/
991
+ if @lp64
992
+ ld_path = 'LD_LIBRARY_PATH'
993
+ else
994
+ ld_path = 'SHLIB_PATH'
995
+ end
996
+ so_ext = 'sl'
997
+ when /darwin/
998
+ ld_path = 'DYLD_LIBRARY_PATH'
999
+ so_ext = 'dylib'
1000
+ else
1001
+ ld_path = 'LD_LIBRARY_PATH'
1002
+ so_ext = 'so'
1003
+ end
1004
+ # check Oracle client library.
1005
+ unless File.exist?("#{lib_dir}/libclntsh.#{so_ext}")
1006
+ files = Dir.glob("#{lib_dir}/libclntsh.#{so_ext}.*")
1007
+ if files.empty?
1008
+ raise <<EOS
1009
+ Could not compile with Oracle instant client.
1010
+ '#{lib_dir}/libclntsh.#{so_ext}' could not be found.
1011
+ Did you install instantclient-basic?
1012
+ EOS
1013
+ else
1014
+ file = File.basename(files.sort[-1])
1015
+ raise <<EOS
1016
+ Could not compile with Oracle instant client.
1017
+ #{lib_dir}/libclntsh.#{so_ext} could not be found.
1018
+ You may need to make a symbolic link.
1019
+ cd #{lib_dir}
1020
+ ln -s #{file} libclntsh.#{so_ext}
1021
+ EOS
1022
+ end
1023
+ raise 'failed'
1024
+ end
1025
+ @libs = get_libs(lib_dir)
1026
+ end
1027
+ unless File.exist?("#{inc_dir}/oci.h")
1028
+ raise <<EOS
1029
+ '#{inc_dir}/oci.h' does not exist.
1030
+ Install 'Instant Client SDK'.
1031
+ EOS
1032
+ end
1033
+ $CFLAGS += @cflags
1034
+ if try_link_oci()
1035
+ major = try_constant("OCI_MAJOR_VERSION", "oci.h")
1036
+ minor = try_constant("OCI_MINOR_VERSION", "oci.h")
1037
+ if major and minor
1038
+ @version = format('%d%d0', major, minor)
1039
+ else
1040
+ # 10.1.0 doesn't have OCI_MAJOR_VERSION and OCI_MINOR_VERSION in oci.h.
1041
+ @version = "1010"
1042
+ if RUBY_PLATFORM =~ /darwin/ and 1.size == 8 and `sw_vers -productVersion`.chomp == "10.7"
1043
+ $stderr.print <<EOS
1044
+ WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN!
1045
+
1046
+ 64-bit Oracle instant client doesn't work on OS X Lion.
1047
+ See: https://forums.oracle.com/forums/thread.jspa?threadID=2187558
1048
+
1049
+ The compilation is continued because the issue may be fixed in future.
1050
+
1051
+ WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN! WARN!
1052
+ EOS
1053
+ end
1054
+ end
1055
+ return
1056
+ end
1057
+
1058
+ if RUBY_PLATFORM =~ /darwin/
1059
+ open('mkmf.log', 'r') do |f|
1060
+ while line = f.gets
1061
+ if line.include? '/libclntsh.dylib load command 8 unknown cmd field'
1062
+ raise <<EOS
1063
+ Intel mac instant client is for Mac OS X 10.5.
1064
+ It doesn't work on Mac OS X 10.4 or before.
1065
+
1066
+ You have three workarounds.
1067
+ 1. Compile ruby as ppc binary and use it with ppc instant client.
1068
+ 2. Use JRuby and JDBC
1069
+ 3. Use a third-party ODBC driver and ruby-odbc.
1070
+ EOS
1071
+ # '
1072
+ end
1073
+
1074
+ case line
1075
+ when /cputype \(\d+, architecture \w+\) does not match cputype \(\d+\) for specified -arch flag: (\w+)/
1076
+ missing_arch = $1
1077
+ when /Undefined symbols for architecture (\w+)/
1078
+ missing_arch = $1
1079
+ when /missing required architecture (\w+) in file/
1080
+ missing_arch = $1
1081
+ end
1082
+
1083
+ if missing_arch
1084
+ if [nil].pack('p').size == 8
1085
+ my_arch = 'x86_64'
1086
+ elsif "\x01\x02".unpack('s')[0] == 0x0201
1087
+ my_arch = 'i386'
1088
+ else
1089
+ my_arch = 'ppc'
1090
+ end
1091
+ raise <<EOS
1092
+ Could not compile with Oracle instant client.
1093
+ You may need to set the environment variable RC_ARCHS or ARCHFLAGS as follows:
1094
+
1095
+ RC_ARCHS=#{my_arch}
1096
+ export RC_ARCHS
1097
+ or
1098
+ ARCHFLAGS='-arch #{my_arch}'
1099
+ export RC_ARCHS
1100
+
1101
+ If it does not fix the problem, delete all '-arch #{missing_arch}'
1102
+ in '#{RbConfig::CONFIG['archdir']}/rbconfig.rb'.
1103
+ EOS
1104
+ end
1105
+ end
1106
+ end
1107
+ end
1108
+
1109
+ unless ld_path.nil?
1110
+ raise <<EOS
1111
+ Could not compile with Oracle instant client.
1112
+ You may need to set a environment variable:
1113
+ #{ld_path}=#{lib_dir}
1114
+ export #{ld_path}
1115
+ EOS
1116
+ end
1117
+ raise 'failed'
1118
+ end
1119
+ end