ruby-oci8 1.0.2

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 (71) 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/MANIFEST +22 -0
  13. data/ext/oci8/attr.c +415 -0
  14. data/ext/oci8/bind.c +194 -0
  15. data/ext/oci8/const.c +165 -0
  16. data/ext/oci8/define.c +53 -0
  17. data/ext/oci8/describe.c +81 -0
  18. data/ext/oci8/descriptor.c +39 -0
  19. data/ext/oci8/env.c +276 -0
  20. data/ext/oci8/error.c +234 -0
  21. data/ext/oci8/extconf.rb +118 -0
  22. data/ext/oci8/handle.c +262 -0
  23. data/ext/oci8/lob.c +386 -0
  24. data/ext/oci8/oci8.c +137 -0
  25. data/ext/oci8/oci8.h +345 -0
  26. data/ext/oci8/ocinumber.c +117 -0
  27. data/ext/oci8/oraconf.rb +1026 -0
  28. data/ext/oci8/oradate.c +426 -0
  29. data/ext/oci8/oranumber.c +445 -0
  30. data/ext/oci8/param.c +37 -0
  31. data/ext/oci8/post-config.rb +5 -0
  32. data/ext/oci8/server.c +182 -0
  33. data/ext/oci8/session.c +99 -0
  34. data/ext/oci8/stmt.c +624 -0
  35. data/ext/oci8/svcctx.c +229 -0
  36. data/lib/DBD/OCI8/OCI8.rb +549 -0
  37. data/lib/oci8.rb.in +1605 -0
  38. data/metaconfig +142 -0
  39. data/pre-distclean.rb +7 -0
  40. data/ruby-oci8.gemspec +54 -0
  41. data/ruby-oci8.spec +62 -0
  42. data/setup.rb +1331 -0
  43. data/support/README +4 -0
  44. data/support/runit/assert.rb +281 -0
  45. data/support/runit/cui/testrunner.rb +101 -0
  46. data/support/runit/error.rb +4 -0
  47. data/support/runit/method_mappable.rb +20 -0
  48. data/support/runit/robserver.rb +25 -0
  49. data/support/runit/setuppable.rb +15 -0
  50. data/support/runit/teardownable.rb +16 -0
  51. data/support/runit/testcase.rb +113 -0
  52. data/support/runit/testfailure.rb +25 -0
  53. data/support/runit/testresult.rb +121 -0
  54. data/support/runit/testsuite.rb +43 -0
  55. data/support/runit/version.rb +3 -0
  56. data/test/README +4 -0
  57. data/test/config.rb +129 -0
  58. data/test/test_all.rb +43 -0
  59. data/test/test_bind_raw.rb +53 -0
  60. data/test/test_bind_time.rb +191 -0
  61. data/test/test_break.rb +81 -0
  62. data/test/test_clob.rb +101 -0
  63. data/test/test_connstr.rb +80 -0
  64. data/test/test_dbi.rb +317 -0
  65. data/test/test_dbi_clob.rb +56 -0
  66. data/test/test_describe.rb +137 -0
  67. data/test/test_metadata.rb +243 -0
  68. data/test/test_oci8.rb +273 -0
  69. data/test/test_oradate.rb +263 -0
  70. data/test/test_oranumber.rb +149 -0
  71. metadata +118 -0
@@ -0,0 +1,117 @@
1
+ /*
2
+ ocinumber.c - part of ruby-oci8
3
+ copy from ocinumber.c in ruby-oci8 0.2.
4
+ */
5
+
6
+ #include "oci8.h"
7
+ #include <orl.h>
8
+
9
+ /* use for local call */
10
+ #define oci_lc(rv) do { \
11
+ sword __rv = (rv); \
12
+ if (__rv != OCI_SUCCESS) { \
13
+ oci8_raise(errhp, __rv, NULL); \
14
+ } \
15
+ } while(0)
16
+
17
+ #define NUMBER_FORMAT1_STR "FM9999999999999999999999990.9999999999999999999999999999999999999"
18
+ #define NUMBER_FORMAT1 (OraText*)NUMBER_FORMAT1_STR
19
+ #define NUMBER_FORMAT1_LEN (sizeof(NUMBER_FORMAT1_STR) - 1)
20
+ #define NUMBER_FORMAT2_STR "FM99999999999999999999999999999999999990.999999999999999999999999"
21
+ #define NUMBER_FORMAT2 (OraText*)NUMBER_FORMAT2_STR
22
+ #define NUMBER_FORMAT2_LEN (sizeof(NUMBER_FORMAT2_STR) - 1)
23
+ #define NUMBER_FORMAT2_DECIMAL (sizeof("999999999999999999999999") - 1)
24
+ #define NUMBER_FORMAT_INT_STR "FM99999999999999999999999999999999999990"
25
+ #define NUMBER_FORMAT_INT (OraText*)NUMBER_FORMAT_INT_STR
26
+ #define NUMBER_FORMAT_INT_LEN (sizeof(NUMBER_FORMAT_INT_STR) - 1)
27
+
28
+ #ifndef StringValue
29
+ #define StringValue(s) ((s) = (TYPE(s) == T_STRING) ? (s) : rb_str_to_str(s))
30
+ #endif
31
+
32
+ /* fill C structure (OCINumber) from a string. */
33
+ static void set_oci_number_from_str(OCINumber *result, VALUE str, VALUE fmt, VALUE nls_params, OCIError *errhp)
34
+ {
35
+ oratext *fmt_ptr;
36
+ oratext *nls_params_ptr;
37
+ ub4 fmt_len;
38
+ ub4 nls_params_len;
39
+
40
+ StringValue(str);
41
+ /* set from string. */
42
+ if (NIL_P(fmt)) {
43
+ int i, cnt = 0;
44
+ for (i = RSTRING_LEN(str) - 1; i >= 0; i--) {
45
+ if (RSTRING_PTR(str)[i] != ' ')
46
+ cnt++;
47
+ if (RSTRING_PTR(str)[i] == '.') {
48
+ i = RSTRING_LEN(str) - i;
49
+ break;
50
+ }
51
+ }
52
+ if (i == -1)
53
+ cnt = 0;
54
+ if (cnt <= NUMBER_FORMAT2_DECIMAL) {
55
+ fmt_ptr = NUMBER_FORMAT2;
56
+ fmt_len = NUMBER_FORMAT2_LEN;
57
+ } else {
58
+ fmt_ptr = NUMBER_FORMAT1;
59
+ fmt_len = NUMBER_FORMAT1_LEN;
60
+ }
61
+ } else {
62
+ StringValue(fmt);
63
+ fmt_ptr = RSTRING_ORATEXT(fmt);
64
+ fmt_len = RSTRING_LEN(fmt);
65
+ }
66
+ if (NIL_P(nls_params)) {
67
+ nls_params_ptr = NULL;
68
+ nls_params_len = 0;
69
+ } else {
70
+ StringValue(nls_params);
71
+ nls_params_ptr = RSTRING_ORATEXT(nls_params);
72
+ nls_params_len = RSTRING_LEN(nls_params);
73
+ }
74
+ oci_lc(OCINumberFromText(errhp,
75
+ RSTRING_ORATEXT(str), RSTRING_LEN(str),
76
+ fmt_ptr, fmt_len, nls_params_ptr, nls_params_len,
77
+ result));
78
+ }
79
+
80
+ /* fill C structure (OCINumber) from a numeric object. */
81
+ /* 1 - success, 0 - error */
82
+ static int set_oci_number_from_num(OCINumber *result, VALUE num, OCIError *errhp)
83
+ {
84
+ signed long sl;
85
+ double dbl;
86
+
87
+ if (!RTEST(rb_obj_is_kind_of(num, rb_cNumeric)))
88
+ rb_raise(rb_eTypeError, "expect Numeric but %s", rb_class2name(CLASS_OF(num)));
89
+ switch (rb_type(num)) {
90
+ case T_FIXNUM:
91
+ /* set from long. */
92
+ sl = NUM2LONG(num);
93
+ oci_lc(OCINumberFromInt(errhp, &sl, sizeof(sl), OCI_NUMBER_SIGNED, result));
94
+ return 1;
95
+ case T_FLOAT:
96
+ /* set from double. */
97
+ dbl = NUM2DBL(num);
98
+ oci_lc(OCINumberFromReal(errhp, &dbl, sizeof(dbl), result));
99
+ return 1;
100
+ case T_BIGNUM:
101
+ /* change via string. */
102
+ num = rb_big2str(num, 10);
103
+ set_oci_number_from_str(result, num, Qnil, Qnil, errhp);
104
+ return 1;
105
+ }
106
+ if (RTEST(rb_obj_is_instance_of(num, cOraNumber))) {
107
+ /* OCI::Number */
108
+ oci_lc(OCINumberAssign(errhp, DATA_PTR(num), result));
109
+ return 1;
110
+ }
111
+ return 0;
112
+ }
113
+
114
+ int set_oci_vnumber(ora_vnumber_t *result, VALUE num, OCIError *errhp)
115
+ {
116
+ return set_oci_number_from_num((OCINumber*)result, num, errhp);
117
+ }
@@ -0,0 +1,1026 @@
1
+ require 'mkmf'
2
+
3
+ unless defined? macro_defined?
4
+ # ruby 1.6 doesn't have 'macro_defined?'.
5
+ def macro_defined?(macro, src, opt="")
6
+ try_cpp(src + <<"SRC", opt)
7
+ #ifndef #{macro}
8
+ # error
9
+ #endif
10
+ SRC
11
+ end
12
+ end
13
+
14
+ module Logging
15
+ unless Logging.respond_to?(:open)
16
+ # emulate Logging::open of ruby 1.6.8 or later.
17
+
18
+ if $log.nil? # ruby 1.6.2 doesn't have $log.
19
+ $log = open('mkmf.log', 'w')
20
+ end
21
+ def Logging::open
22
+ begin
23
+ $stderr.reopen($log)
24
+ $stdout.reopen($log)
25
+ yield
26
+ ensure
27
+ $stderr.reopen($orgerr)
28
+ $stdout.reopen($orgout)
29
+ end
30
+ end
31
+ end
32
+ end # module Logging
33
+
34
+ module MiniRegistry
35
+ class MiniRegistryError < StandardError
36
+ attr_reader :api_name
37
+ attr_reader :code
38
+ def initialize(api_name, code)
39
+ @api_name = api_name
40
+ @code = code
41
+ end
42
+ end
43
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
44
+ # Windows
45
+ require 'Win32API' # raise LoadError when UNIX.
46
+
47
+ # I looked in Win32Module by MoonWolf <URL:http://www.moonwolf.com/ruby/>,
48
+ # copy the minimum code and reorganize it.
49
+ ERROR_SUCCESS = 0
50
+ ERROR_FILE_NOT_FOUND = 2
51
+
52
+ HKEY_LOCAL_MACHINE = 0x80000002
53
+ RegOpenKeyExA = Win32API.new('advapi32', 'RegOpenKeyExA', 'LPLLP', 'L')
54
+ RegQueryValueExA = Win32API.new('advapi32','RegQueryValueExA','LPPPPP','L')
55
+ RegCloseKey = Win32API.new('advapi32', 'RegCloseKey', 'L', 'L')
56
+
57
+ def get_reg_value(root, subkey, name)
58
+ result = [0].pack('L')
59
+ code = RegOpenKeyExA.call(root, subkey, 0, 0x20019, result)
60
+ if code != ERROR_SUCCESS
61
+ raise MiniRegistryError.new("Win32::RegOpenKeyExA", code)
62
+ end
63
+ hkey = result.unpack('L')[0]
64
+ begin
65
+ lpcbData = [0].pack('L')
66
+ code = RegQueryValueExA.call(hkey, name, nil, nil, nil, lpcbData)
67
+ if code == ERROR_FILE_NOT_FOUND
68
+ return nil
69
+ elsif code != ERROR_SUCCESS
70
+ raise MiniRegistryError.new("Win32::RegQueryValueExA",code)
71
+ end
72
+ len = lpcbData.unpack('L')[0]
73
+ lpType = "\0\0\0\0"
74
+ lpData = "\0"*len
75
+ lpcbData = [len].pack('L')
76
+ code = RegQueryValueExA.call(hkey, name, nil, lpType, lpData, lpcbData)
77
+ if code != ERROR_SUCCESS
78
+ raise MiniRegistryError.new("Win32::RegQueryValueExA",code)
79
+ end
80
+ lpData.unpack('Z*')[0]
81
+ ensure
82
+ RegCloseKey.call(hkey)
83
+ end
84
+ end
85
+ def get_local_registry(subkey, name)
86
+ get_reg_value(HKEY_LOCAL_MACHINE, subkey, name)
87
+ end
88
+ else
89
+ # UNIX
90
+ def get_local_registry(subkey, name)
91
+ nil
92
+ end
93
+ end
94
+ end # module MiniRegistry
95
+
96
+ # minimal implementation to read information of a shared object.
97
+ class MiniSOReader
98
+ attr_reader :file_format
99
+ attr_reader :cpu
100
+ attr_reader :endian
101
+ attr_reader :bits
102
+
103
+ def initialize(filename)
104
+ f = open(filename, 'rb')
105
+ begin
106
+ case file_header = f.read(2)
107
+ when "\177E"
108
+ # Linux, Solaris and HP-UX(64-bit)
109
+ read_elf(f) if f.read(2) == 'LF'
110
+ when "MZ"
111
+ # Windows
112
+ read_pe(f)
113
+ else
114
+ # HP-UX(32-bit), AIX, Mac OS X and Tru64
115
+ raise format("unknown file header: %02x %02x", file_header[0], file_header[1])
116
+ end
117
+ ensure
118
+ f.close
119
+ end
120
+ end
121
+
122
+ private
123
+ # ELF format
124
+ def read_elf(f)
125
+ # 0-3 "\177ELF"
126
+ @file_format = :elf
127
+ # 4
128
+ case f.read(1).unpack('C')[0]
129
+ when 1
130
+ @bits = 32
131
+ when 2
132
+ @bits = 64
133
+ else
134
+ raise 'Invalid ELF class'
135
+ end
136
+ # 5
137
+ case f.read(1).unpack('C')[0]
138
+ when 1
139
+ @endian = :little
140
+ pack_type_short = 'v'
141
+ when 2
142
+ @endian = :big
143
+ pack_type_short = 'n'
144
+ else
145
+ raise 'Invalid ELF byte order'
146
+ end
147
+ # 6
148
+ raise 'Invalid ELF header version' if f.read(1) != "\x01"
149
+ # 16-17
150
+ f.seek(16, IO::SEEK_SET)
151
+ raise 'Invalid ELF filetype' if f.read(2).unpack(pack_type_short)[0] != 3
152
+ # 18-19
153
+ case archtype = f.read(2).unpack(pack_type_short)[0]
154
+ when 2
155
+ @cpu = :sparc
156
+ when 3
157
+ @cpu = :i386
158
+ when 15
159
+ @cpu = :parisc
160
+ when 20
161
+ @cpu = :ppc
162
+ when 21
163
+ @cpu = :ppc64
164
+ when 22
165
+ @cpu = :s390
166
+ when 43
167
+ @cpu = :sparcv9
168
+ when 50
169
+ @cpu = :ia64
170
+ when 62
171
+ @cpu = :x86_64
172
+ else
173
+ raise "Invalid ELF archtype: #{archtype}"
174
+ end
175
+ end
176
+
177
+ # PE/COFF format
178
+ def read_pe(f)
179
+ # 0-1 "MZ"
180
+ @file_format = :pe
181
+ # 60-63
182
+ f.seek(60, IO::SEEK_SET)
183
+ pe_offset = f.read(4).unpack('V')[0]
184
+ # read PE signature
185
+ f.seek(pe_offset)
186
+ raise 'invalid pe format' if f.read(4) != "PE\000\000"
187
+ # read COFF header
188
+ case machine = f.read(2).unpack('v')[0]
189
+ when 0x014c
190
+ @cpu = :i386
191
+ @endian = :little
192
+ @bits = 32
193
+ when 0x0200
194
+ @cpu = :ia64
195
+ @endian = :little
196
+ @bits = 64
197
+ when 0x8664
198
+ @cpu = :x86_64
199
+ @endian = :little
200
+ @bits = 64
201
+ else
202
+ raise "Invalid coff machine: #{machine}"
203
+ end
204
+ end
205
+ end
206
+
207
+ class OraConf
208
+ include MiniRegistry
209
+
210
+ attr_reader :cc_is_gcc
211
+ attr_reader :version
212
+ attr_reader :cflags
213
+ attr_reader :libs
214
+
215
+ def initialize
216
+ raise 'use OraConf.get instead'
217
+ end
218
+
219
+ def self.get
220
+ original_CFLAGS = $CFLAGS
221
+ original_defs = $defs
222
+ ic_dir = nil
223
+ begin
224
+ # check Oracle instant client
225
+ if with_config('instant-client')
226
+ puts <<EOS
227
+ =======================================================
228
+
229
+ '--with-instant-client' is an obsolete option. ignore it.
230
+
231
+ =======================================================
232
+ EOS
233
+ end
234
+ ic_dir = check_ic_dir
235
+ if ic_dir
236
+ OraConfIC.new(ic_dir)
237
+ else
238
+ OraConfFC.new()
239
+ end
240
+ rescue
241
+ case ENV['LANG']
242
+ when /^ja/
243
+ lang = 'ja'
244
+ else
245
+ lang = 'en'
246
+ end
247
+ print <<EOS
248
+ ---------------------------------------------------
249
+ error messages:
250
+ #{$!.to_str}
251
+ ---------------------------------------------------
252
+ See:
253
+ * http://ruby-oci8.rubyforge.org/#{lang}/HowToInstall.html
254
+ * http://ruby-oci8.rubyforge.org/#{lang}/ReportInstallProblem.html
255
+
256
+ EOS
257
+ exc = RuntimeError.new
258
+ exc.set_backtrace($!.backtrace)
259
+ raise exc
260
+ ensure
261
+ $CFLAGS = original_CFLAGS
262
+ $defs = original_defs
263
+ end
264
+ end
265
+
266
+ def self.ld_envs
267
+ @@ld_envs
268
+ end
269
+
270
+ private
271
+
272
+ def self.check_ic_dir
273
+ print "checking for load library path... "
274
+ STDOUT.flush
275
+
276
+ # get library load path names
277
+ oci_basename = 'libclntsh'
278
+ oci_glob_postfix = '.[0-9]*'
279
+ ocidata_basename = ['libociei', 'libociicus']
280
+ @@ld_envs = %w[LD_LIBRARY_PATH]
281
+ so_ext = 'so'
282
+ check_proc = nil
283
+ case RUBY_PLATFORM
284
+ when /mswin32|cygwin|mingw32|bccwin32/
285
+ oci_basename = 'oci'
286
+ oci_glob_postfix = ''
287
+ ocidata_basename = ['oraociei11', 'oraociicus11', 'oraociei10', 'oraociicus10']
288
+ @@ld_envs = %w[PATH]
289
+ so_ext = 'dll'
290
+ when /i.86-linux/
291
+ check_proc = Proc.new do |file|
292
+ so = MiniSOReader.new(file)
293
+ if so.cpu == :i386
294
+ true
295
+ else
296
+ puts " skip: #{file} is for #{so.cpu} cpu."
297
+ false
298
+ end
299
+ end
300
+ when /ia64-linux/
301
+ check_proc = Proc.new do |file|
302
+ so = MiniSOReader.new(file)
303
+ if so.cpu == :ia64
304
+ true
305
+ else
306
+ puts " skip: #{file} is for #{so.cpu} cpu."
307
+ false
308
+ end
309
+ end
310
+ when /x86_64-linux/
311
+ check_proc = Proc.new do |file|
312
+ so = MiniSOReader.new(file)
313
+ if so.cpu == :x86_64
314
+ true
315
+ else
316
+ puts " skip: #{file} is for #{so.cpu} cpu."
317
+ false
318
+ end
319
+ end
320
+ when /solaris/
321
+ if [0].pack('l!').length == 8
322
+ @@ld_envs = %w[LD_LIBRARY_PATH_64 LD_LIBRARY_PATH]
323
+ else
324
+ @@ld_envs = %w[LD_LIBRARY_PATH_32 LD_LIBRARY_PATH]
325
+ end
326
+ when /aix/
327
+ oci_glob_postfix = ''
328
+ @@ld_envs = %w[LIBPATH]
329
+ so_ext = 'a'
330
+ when /hppa.*-hpux/
331
+ if [0].pack('l!').length == 4
332
+ @@ld_envs = %w[SHLIB_PATH]
333
+ end
334
+ so_ext = 'sl'
335
+ when /darwin/
336
+ @@ld_envs = %w[DYLD_LIBRARY_PATH]
337
+ so_ext = 'dylib'
338
+ end
339
+
340
+ glob_name = "#{oci_basename}.#{so_ext}#{oci_glob_postfix}"
341
+ ld_path = nil
342
+ file = nil
343
+ @@ld_envs.collect do |env|
344
+ puts "(#{env})... "
345
+ ENV[env] && ENV[env].split(File::PATH_SEPARATOR)
346
+ end.flatten.each do |path|
347
+ next if path.nil? or path == ''
348
+ path.gsub!(/\\/, '/') if /mswin32|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
349
+ files = Dir.glob(File.join(path, glob_name))
350
+ next if files.empty?
351
+ STDOUT.flush
352
+ next if (check_proc && !check_proc.call(files[0]))
353
+ file = files[0]
354
+ ld_path = path
355
+ break
356
+ end
357
+
358
+ if ld_path.nil? and RUBY_PLATFORM =~ /linux/
359
+ open("|/sbin/ldconfig -p") do |f|
360
+ print "(ld.so.conf)... "
361
+ STDOUT.flush
362
+ while line = f.gets
363
+ if line =~ /libclntsh\.so\..* => (\/.*)\/libclntsh\.so\.(.*)/
364
+ file = "#$1/libclntsh.so.#$2"
365
+ path = $1
366
+ next if (check_proc && !check_proc.call(file))
367
+ ld_path = path
368
+ break
369
+ end
370
+ end
371
+ end
372
+ end
373
+
374
+ if ld_path
375
+ ocidata_basename.each do |basename|
376
+ if File.exist?(File.join(ld_path, "#{basename}.#{so_ext}"))
377
+ puts " found: #{file} looks like an instant client."
378
+ return ld_path
379
+ end
380
+ end
381
+ puts " found: #{file} looks like a full client."
382
+ else
383
+ puts " not found"
384
+ end
385
+ nil
386
+ end
387
+
388
+ def init
389
+ check_cc()
390
+ @cc_is_gcc = check_cc_is_gcc()
391
+ @lp64 = check_lp64()
392
+ check_ruby_header()
393
+ end
394
+
395
+ def check_cc
396
+ print "checking for cc... "
397
+ STDOUT.flush
398
+ if try_run("int main() { return 0; }")
399
+ puts "ok"
400
+ else
401
+ puts "ng"
402
+ raise "C compiler doesn't work correctly."
403
+ end
404
+ end # check_cc
405
+
406
+ def check_cc_is_gcc
407
+ # bcc defines __GNUC__. why??
408
+ return false if RUBY_PLATFORM =~ /bccwin32/
409
+
410
+ print "checking for gcc... "
411
+ STDOUT.flush
412
+ if macro_defined?("__GNUC__", "")
413
+ print "yes\n"
414
+ return true
415
+ else
416
+ print "no\n"
417
+ return false
418
+ end
419
+ end # check_cc_is_gcc
420
+
421
+ def check_lp64
422
+ print "checking for LP64... "
423
+ STDOUT.flush
424
+ if try_run("int main() { return sizeof(long) == 8 ? 0 : 1; }")
425
+ puts "yes"
426
+ true
427
+ else
428
+ puts "no"
429
+ false
430
+ end
431
+ end # check_lp64
432
+
433
+ def check_ruby_header
434
+ print "checking for ruby header... "
435
+ STDOUT.flush
436
+ unless File.exist?("#{Config::CONFIG['archdir']}/ruby.h")
437
+ puts "ng"
438
+ if RUBY_PLATFORM =~ /darwin/ and File.exist?("#{Config::CONFIG['archdir']}/../universal-darwin8.0/ruby.h")
439
+ raise <<EOS
440
+ #{Config::CONFIG['archdir']}/ruby.h doesn't exist.
441
+ Run the following commands to fix the problem.
442
+
443
+ cd #{Config::CONFIG['archdir']}
444
+ sudo ln -s ../universal-darwin8.0/* ./
445
+ EOS
446
+ else
447
+ raise <<EOS
448
+ #{Config::CONFIG['archdir']}/ruby.h doesn't exist.
449
+ Install the ruby development library.
450
+ EOS
451
+ end
452
+ end
453
+ if RUBY_PLATFORM =~ /linux/ and not File.exist?("/usr/include/sys/types.h")
454
+ raise <<EOS
455
+ Do you install glibc-devel(redhat) or libc6-dev(debian)?
456
+ You need /usr/include/sys/types.h to compile ruby-oci8.
457
+ EOS
458
+ end
459
+ puts "ok"
460
+ end # check_ruby_header
461
+
462
+ def try_link_oci
463
+ original_libs = $libs
464
+ begin
465
+ $libs += " -L#{CONFIG['libdir']} " + @libs
466
+ have_func("OCIInitialize", "oci.h")
467
+ ensure
468
+ $libs = original_libs
469
+ end
470
+ end
471
+
472
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
473
+
474
+ def get_libs(base_dir = oci_base_dir)
475
+ case RUBY_PLATFORM
476
+ when /cygwin/
477
+ open("OCI.def", "w") do |f|
478
+ f.puts("EXPORTS")
479
+ open("|nm #{base_dir}/LIB/MSVC/OCI.LIB") do |r|
480
+ while line = r.gets
481
+ f.puts($') if line =~ / T _/
482
+ end
483
+ end
484
+ end
485
+ command = "dlltool -d OCI.def -D OCI.DLL -l libOCI.a"
486
+ print("Running: '#{command}' ...")
487
+ STDOUT.flush
488
+ system(command)
489
+ puts("done")
490
+ "-L. -lOCI"
491
+ when /bccwin32/
492
+ # replace '/' to '\\' because bcc linker misunderstands
493
+ # 'C:/foo/bar/OCI.LIB' as unknown option.
494
+ lib = "#{base_dir}/LIB/BORLAND/OCI.LIB"
495
+ return lib.tr('/', '\\') if File.exist?(lib)
496
+ raise <<EOS
497
+ #{lib} does not exist.
498
+
499
+ Your Oracle may not support Borland C++.
500
+ If you want to run this module, run the following command at your own risk.
501
+ cd #{base_dir.tr('/', '\\')}\\LIB
502
+ mkdir Borland
503
+ cd Borland
504
+ coff2omf ..\\MSVC\\OCI.LIB OCI.LIB
505
+ EOS
506
+ exit 1
507
+ else
508
+ "\"#{base_dir}/LIB/MSVC/OCI.LIB\""
509
+ end
510
+ end
511
+
512
+ end
513
+ end
514
+
515
+ # OraConf for Full Client
516
+ class OraConfFC < OraConf
517
+ def initialize
518
+ init
519
+
520
+ @oracle_home = get_home()
521
+ if RUBY_PLATFORM =~ /freebsd/ && @oracle_home == '/usr/local/oracle8-client'
522
+ @version = '817'
523
+ else
524
+ @version = get_version()
525
+ end
526
+ @cflags = get_cflags()
527
+ $CFLAGS += @cflags
528
+
529
+ if !@lp64 && File.exist?("#{@oracle_home}/lib32")
530
+ # ruby - 32bit
531
+ # oracle - 64bit
532
+ use_lib32 = true
533
+ else
534
+ use_lib32 = false
535
+ end
536
+
537
+ # default
538
+ if @version.to_i >= 900
539
+ if use_lib32
540
+ lib_dir = "#{@oracle_home}/lib32"
541
+ else
542
+ lib_dir = "#{@oracle_home}/lib"
543
+ end
544
+ case RUBY_PLATFORM
545
+ when /solaris/
546
+ @libs = " -L#{lib_dir} -R#{lib_dir} -lclntsh"
547
+ when /linux/
548
+ @libs = " -L#{lib_dir} -Wl,-rpath,#{lib_dir} -lclntsh"
549
+ else
550
+ @libs = " -L#{lib_dir} -lclntsh"
551
+ end
552
+ return if try_link_oci()
553
+ end
554
+
555
+ # get from demo_rdbms.mk
556
+ if use_lib32
557
+ if File.exist?("#{@oracle_home}/rdbms/demo/demo_rdbms32.mk")
558
+ @libs = get_libs('32', '')
559
+ else
560
+ @libs = get_libs('', '32')
561
+ end
562
+ else
563
+ @libs = get_libs()
564
+ end
565
+ return if try_link_oci()
566
+
567
+ raise 'cannot compile OCI'
568
+ end
569
+
570
+ private
571
+
572
+ def get_version
573
+ print("Get the version of Oracle from SQL*Plus... ")
574
+ STDOUT.flush
575
+ version = nil
576
+ dev_null = RUBY_PLATFORM =~ /mswin32|mingw32|bccwin32/ ? "nul" : "/dev/null"
577
+ if File.exist?("#{@oracle_home}/bin/plus80.exe")
578
+ sqlplus = "plus80.exe"
579
+ else
580
+ sqlplus = "sqlplus"
581
+ end
582
+ Logging::open do
583
+ open("|#{@oracle_home}/bin/#{sqlplus} < #{dev_null}") do |f|
584
+ while line = f.gets
585
+ print line
586
+ if line =~ /(\d+)\.(\d)\.(\d)/
587
+ version = $1 + $2 + $3
588
+ break
589
+ end
590
+ end
591
+ end
592
+ end
593
+ if version.nil?
594
+ raise 'cannot get Oracle version from sqlplus'
595
+ end
596
+ puts version
597
+ version
598
+ end # get_version
599
+
600
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
601
+
602
+ def is_valid_home?(oracle_home)
603
+ return false if oracle_home.nil?
604
+ sqlplus = "#{oracle_home}/bin/sqlplus.exe"
605
+ print("checking for ORACLE_HOME(#{oracle_home})... ")
606
+ STDOUT.flush
607
+ if File.exist?(sqlplus)
608
+ puts("yes")
609
+ true
610
+ else
611
+ puts("no")
612
+ false
613
+ end
614
+ end
615
+
616
+ def get_home()
617
+ oracle_home = ENV['ORACLE_HOME']
618
+ if oracle_home.nil?
619
+ struct = Struct.new("OracleHome", :name, :path)
620
+ oracle_homes = []
621
+ begin
622
+ last_home = get_local_registry("SOFTWARE\\ORACLE\\ALL_HOMES", 'LAST_HOME')
623
+ 0.upto last_home.to_i do |id|
624
+ oracle_homes << "HOME#{id}"
625
+ end
626
+ rescue MiniRegistryError
627
+ end
628
+ oracle_homes << "KEY_XE"
629
+ oracle_homes << "KEY_XEClient"
630
+ oracle_homes.collect! do |home|
631
+ begin
632
+ name = get_local_registry("SOFTWARE\\ORACLE\\#{home}", 'ORACLE_HOME_NAME')
633
+ path = get_local_registry("SOFTWARE\\ORACLE\\#{home}", 'ORACLE_HOME')
634
+ path.chomp!("\\")
635
+ struct.new(name, path) if is_valid_home?(path)
636
+ rescue MiniRegistryError
637
+ nil
638
+ end
639
+ end
640
+ oracle_homes.compact!
641
+ if oracle_homes.empty?
642
+ raise <<EOS
643
+ Set the environment variable ORACLE_HOME if Oracle Full Client.
644
+ Append the path of Oracle client libraries to #{OraConf.ld_envs[0]} if Oracle Instant Client.
645
+ EOS
646
+ end
647
+ if oracle_homes.length == 1
648
+ oracle_home = oracle_homes[0].path
649
+ else
650
+ default_path = ''
651
+ if RUBY_PLATFORM =~ /cygwin/
652
+ path_sep = ':'
653
+ dir_sep = '/'
654
+ else
655
+ path_sep = ';'
656
+ dir_sep = '\\'
657
+ end
658
+ ENV['PATH'].split(path_sep).each do |path|
659
+ path.chomp!(dir_sep)
660
+ if File.exist?("#{path}/OCI.DLL")
661
+ default_path = path
662
+ break
663
+ end
664
+ end
665
+ puts "---------------------------------------------------"
666
+ puts "Multiple Oracle Homes are found."
667
+ printf " %-15s : %s\n", "[NAME]", "[PATH]"
668
+ oracle_homes.each do |home|
669
+ if RUBY_PLATFORM =~ /cygwin/
670
+ path = `cygpath -u '#{home.path}'`.chomp!
671
+ else
672
+ path = home.path
673
+ end
674
+ if default_path.downcase == "#{path.downcase}#{dir_sep}bin"
675
+ oracle_home = home
676
+ end
677
+ printf " %-15s : %s\n", home.name, home.path
678
+ end
679
+ if oracle_home.nil?
680
+ puts "default oracle home is not found."
681
+ puts "---------------------------------------------------"
682
+ raise 'Cannot get ORACLE_HOME. Please set the environment valiable ORACLE_HOME.'
683
+ else
684
+ printf "use %s\n", oracle_home.name
685
+ puts "run ohsel.exe to use another oracle home."
686
+ puts "---------------------------------------------------"
687
+ oracle_home = oracle_home.path
688
+ end
689
+ end
690
+ end
691
+ if RUBY_PLATFORM =~ /cygwin/
692
+ oracle_home = oracle_home.sub(/^([a-zA-Z]):/, "/cygdrive/\\1")
693
+ end
694
+ oracle_home.gsub(/\\/, '/')
695
+ end
696
+
697
+ def oci_base_dir
698
+ case @version
699
+ when /80./
700
+ "#{@oracle_home}/OCI80"
701
+ else
702
+ "#{@oracle_home}/OCI"
703
+ end
704
+ end
705
+
706
+ def get_cflags
707
+ unless File.exist?("#{oci_base_dir}/INCLUDE/OCI.H")
708
+ raise "'#{oci_base_dir}/INCLUDE/OCI.H' does not exists. Please install 'Oracle Call Interface'."
709
+ end
710
+ if RUBY_PLATFORM =~ /cygwin/
711
+ " \"-I#{oci_base_dir}/INCLUDE\" -D_int64=\"long long\""
712
+ else
713
+ " \"-I#{oci_base_dir}/INCLUDE\""
714
+ end
715
+ end
716
+
717
+ else # when UNIX
718
+
719
+ def get_home
720
+ oracle_home = ENV['ORACLE_HOME']
721
+ if oracle_home.nil?
722
+ raise <<EOS
723
+ Set the environment variable ORACLE_HOME if Oracle Full Client.
724
+ Append the path of Oracle client libraries to #{OraConf.ld_envs[0]} if Oracle Instant Client.
725
+ EOS
726
+ end
727
+ oracle_home
728
+ end
729
+
730
+ def get_cflags
731
+ cflags = ''
732
+ ok = false
733
+ original_CFLAGS = $CFLAGS
734
+ begin
735
+ for i in ["rdbms/public", "rdbms/demo", "network/public", "plsql/public"]
736
+ cflags += " -I#{@oracle_home}/#{i}"
737
+ $CFLAGS += " -I#{@oracle_home}/#{i}"
738
+ print("try #{cflags}\n");
739
+ if have_header("oci.h")
740
+ ok = true
741
+ break
742
+ end
743
+ end
744
+ unless ok
745
+ if @version.to_i >= 1000
746
+ oci_h = "#{@oracle_home}/rdbms/public/oci.h"
747
+ else
748
+ oci_h = "#{@oracle_home}/rdbms/demo/oci.h"
749
+ end
750
+ unless File.exist?(oci_h)
751
+ raise "'#{oci_h}' does not exists. Install 'Oracle Call Interface' component."
752
+ end
753
+ raise 'Cannot get proper cflags.'
754
+ end
755
+ cflags
756
+ ensure
757
+ $CFLAGS = original_CFLAGS
758
+ end
759
+ end # get_cflags
760
+
761
+ def get_libs(postfix1 = '', postfix2 = "")
762
+ print("Running make for $ORACLE_HOME/rdbms/demo/demo_rdbms#{postfix1}.mk (build#{postfix2}) ...")
763
+ STDOUT.flush
764
+
765
+ make_opt = "CC='echo MARKER' EXE=/dev/null ECHODO=echo ECHO=echo GENCLNTSH='echo genclntsh'"
766
+ if @cc_is_gcc && /solaris/ =~ RUBY_PLATFORM
767
+ # suggested by Brian Candler.
768
+ make_opt += " KPIC_OPTION= NOKPIC_CCFLAGS#{postfix2}="
769
+ end
770
+
771
+ command = "|make -f #{@oracle_home}/rdbms/demo/demo_rdbms#{postfix1}.mk build#{postfix2} #{make_opt}"
772
+ marker = /^\s*MARKER/
773
+ echo = /^\s*echo/
774
+ libs = nil
775
+ Logging::open do
776
+ puts command
777
+ open(command, "r") do |f|
778
+ while line = f.gets
779
+ puts line
780
+ line.chomp!
781
+ line = $' while line =~ echo
782
+ if line =~ marker
783
+ # found a line where a compiler runs.
784
+ libs = $'
785
+ libs.gsub!(/-o\s+\/dev\/null/, "")
786
+ libs.gsub!(/-o\s+build/, "")
787
+ end
788
+ end
789
+ end
790
+ end
791
+ raise 'Cannot get proper libs.' if libs.nil?
792
+ print("OK\n")
793
+
794
+ case RUBY_PLATFORM
795
+ when /hpux/
796
+ if @cc_is_gcc
797
+ # strip +DA2.0W, +DS2.0, -Wl,+s, -Wl,+n
798
+ libs.gsub!(/\+DA\S+(\s)*/, "")
799
+ libs.gsub!(/\+DS\S+(\s)*/, "")
800
+ libs.gsub!(/-Wl,\+[sn](\s)*/, "")
801
+ end
802
+ libs.gsub!(/ -Wl,/, " ")
803
+ when /aix/
804
+ if @cc_is_gcc
805
+ # strip -bI:/xxx
806
+ libs.gsub!(/(-bI:\S+)/, '')
807
+ end
808
+ end
809
+
810
+ # remove object files from libs.
811
+ objs = []
812
+ libs.gsub!(/\S+\.o\b/) do |obj|
813
+ objs << obj
814
+ ""
815
+ end
816
+ # change object files to an archive file to work around.
817
+ if objs.length > 0
818
+ Logging::open do
819
+ puts "change object files to an archive file."
820
+ command = Config::CONFIG["AR"] + " cru oracle_objs.a " + objs.join(" ")
821
+ puts command
822
+ system(command)
823
+ libs = "oracle_objs.a " + libs
824
+ end
825
+ end
826
+ libs
827
+ end # get_libs
828
+ end
829
+ end
830
+
831
+ # OraConf for Instant Client
832
+ class OraConfIC < OraConf
833
+ def initialize(ic_dir)
834
+ init
835
+
836
+ if ic_dir =~ /^\/usr\/lib(?:64)?\/oracle\/(\d+\.\d+\.\d+\.\d+)\/client(64)?\/lib(?:64)?/
837
+ # rpm package
838
+ # official x86 rpms:
839
+ # library: /usr/lib/oracle/X.X.X.X/client/lib/
840
+ # include: /usr/include/oracle/X.X.X.X/client/
841
+ #
842
+ # official x86_64 rpms:
843
+ # library: /usr/lib/oracle/X.X.X.X/client64/lib/
844
+ # include: /usr/include/oracle/X.X.X.X/client64/
845
+ #
846
+ # third-party x86_64 rpms(*1):
847
+ # library: /usr/lib64/oracle/X.X.X.X/client/lib/
848
+ # or /usr/lib64/oracle/X.X.X.X/client/lib64/
849
+ # include: /usr/include/oracle/X.X.X.X/client/
850
+ #
851
+ # *1 These had been used before Oracle released official x86_64 rpms.
852
+ #
853
+ lib_dir = ic_dir
854
+ inc_dir = "/usr/include/oracle/#{$1}/client#{$2}"
855
+ else
856
+ # zip package
857
+ lib_dir = ic_dir
858
+ inc_dir = "#{ic_dir}/sdk/include"
859
+ end
860
+
861
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
862
+ unless File.exist?("#{ic_dir}/sdk/lib/msvc/oci.lib")
863
+ raise <<EOS
864
+ Could not compile with Oracle instant client.
865
+ #{ic_dir}/sdk/lib/msvc/oci.lib could not be found.
866
+ EOS
867
+ raise 'failed'
868
+ end
869
+ @cflags = " \"-I#{inc_dir}\""
870
+ @cflags += " -D_int64=\"long long\"" if RUBY_PLATFORM =~ /cygwin/
871
+ @libs = get_libs("#{ic_dir}/sdk")
872
+ ld_path = nil
873
+ else
874
+ @cflags = " -I#{inc_dir}"
875
+ # set ld_path and so_ext
876
+ case RUBY_PLATFORM
877
+ when /aix/
878
+ ld_path = 'LIBPATH'
879
+ so_ext = 'a'
880
+ when /hppa.*-hpux/
881
+ if @lp64
882
+ ld_path = 'LD_LIBRARY_PATH'
883
+ else
884
+ ld_path = 'SHLIB_PATH'
885
+ end
886
+ so_ext = 'sl'
887
+ when /darwin/
888
+ ld_path = 'DYLD_LIBRARY_PATH'
889
+ so_ext = 'dylib'
890
+ else
891
+ ld_path = 'LD_LIBRARY_PATH'
892
+ so_ext = 'so'
893
+ end
894
+ # check Oracle client library.
895
+ unless File.exist?("#{lib_dir}/libclntsh.#{so_ext}")
896
+ files = Dir.glob("#{lib_dir}/libclntsh.#{so_ext}.*")
897
+ if files.empty?
898
+ raise <<EOS
899
+ Could not compile with Oracle instant client.
900
+ '#{lib_dir}/libclntsh.#{so_ext}' could not be found.
901
+ Did you install instantclient-basic?
902
+ EOS
903
+ else
904
+ file = File.basename(files.sort[-1])
905
+ raise <<EOS
906
+ Could not compile with Oracle instant client.
907
+ #{lib_dir}/libclntsh.#{so_ext} could not be found.
908
+ You may need to make a symbolic link.
909
+ cd #{lib_dir}
910
+ ln -s #{file} libclntsh.#{so_ext}
911
+ EOS
912
+ end
913
+ raise 'failed'
914
+ end
915
+ @libs = " -L#{lib_dir} -lclntsh "
916
+ end
917
+ unless File.exist?("#{inc_dir}/oci.h")
918
+ raise <<EOS
919
+ '#{inc_dir}/oci.h' does not exist.
920
+ Install 'Instant Client SDK'.
921
+ EOS
922
+ end
923
+ $CFLAGS += @cflags
924
+ if try_link_oci()
925
+ major = try_constant("OCI_MAJOR_VERSION", "oci.h")
926
+ minor = try_constant("OCI_MINOR_VERSION", "oci.h")
927
+ if major and minor
928
+ @version = format('%d%d0', major, minor)
929
+ else
930
+ # 10.1.0 doesn't have OCI_MAJOR_VERSION and OCI_MINOR_VERSION in oci.h.
931
+ @version = "1010"
932
+ end
933
+ return
934
+ end
935
+
936
+ if RUBY_PLATFORM =~ /darwin/
937
+ is_intelmac = ([1].pack('s') == "\001\000")
938
+ arch_ppc_error = false
939
+ arch_i386_error = false
940
+ open('mkmf.log', 'r') do |f|
941
+ while line = f.gets
942
+ # universal-darwin8.0 (Mac OS X 10.4?)
943
+ if line.include? 'cputype (18, architecture ppc) does not match cputype (7)'
944
+ # try to link an i386 library but the instant client is ppc.
945
+ arch_i386_error = true
946
+ end
947
+ if line.include? 'cputype (7, architecture i386) does not match cputype (18)'
948
+ # try to link a ppc library but the instant client is i386.
949
+ arch_ppc_error = true
950
+ end
951
+ if line.include? '/libclntsh.dylib load command 8 unknown cmd field'
952
+ raise <<EOS
953
+ Intel mac instant client is for Mac OS X 10.5.
954
+ It doesn't work on Mac OS X 10.4 or before.
955
+
956
+ You have three workarounds.
957
+ 1. Compile ruby as ppc binary and use it with ppc instant client.
958
+ 2. Use JRuby and JDBC
959
+ 3. Use a third-party ODBC driver and ruby-odbc.
960
+ EOS
961
+ # '
962
+ end
963
+ # universal-darwin9.0 (Mac OS X 10.5?)
964
+ if line.include? 'Undefined symbols for architecture i386:'
965
+ # try to link an i386 library but the instant client is ppc.
966
+ arch_i386_error = true
967
+ end
968
+ if line.include? 'Undefined symbols for architecture ppc:'
969
+ # try to link a ppc library but the instant client is i386.
970
+ arch_ppc_error = true
971
+ end
972
+
973
+ if arch_i386_error
974
+ if is_intelmac
975
+ # intel mac and '-arch i386' error
976
+ raise <<EOS
977
+ Could not compile with Oracle instant client.
978
+ Use intel mac instant client.
979
+ EOS
980
+ else
981
+ # ppc mac and '-arch i386' error
982
+ raise <<EOS
983
+ Could not compile with Oracle instant client.
984
+ You may need to set a environment variable:
985
+ RC_ARCHS=ppc
986
+ export RC_ARCHS
987
+ If it does not fix the problem, delete all '-arch i386'
988
+ in '#{Config::CONFIG['archdir']}/rbconfig.rb'.
989
+ EOS
990
+ end
991
+ end
992
+
993
+ if arch_ppc_error
994
+ if is_intelmac
995
+ # intel mac and '-arch ppc' error
996
+ raise <<EOS
997
+ Could not compile with Oracle instant client.
998
+ You may need to set a environment variable:
999
+ RC_ARCHS=i386
1000
+ export RC_ARCHS
1001
+ If it does not fix the problem, delete all '-arch ppc'
1002
+ in '#{Config::CONFIG['archdir']}/rbconfig.rb'.
1003
+ EOS
1004
+ else
1005
+ # ppc mac and '-arch ppc' error
1006
+ raise <<EOS
1007
+ Could not compile with Oracle instant client.
1008
+ Use ppc instant client.
1009
+ EOS
1010
+ end
1011
+ end
1012
+ end
1013
+ end
1014
+ end
1015
+
1016
+ unless ld_path.nil?
1017
+ raise <<EOS
1018
+ Could not compile with Oracle instant client.
1019
+ You may need to set a environment variable:
1020
+ #{ld_path}=#{lib_dir}
1021
+ export #{ld_path}
1022
+ EOS
1023
+ end
1024
+ raise 'failed'
1025
+ end
1026
+ end