ruby-oci8 1.0.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/ChangeLog +1254 -390
  2. data/Makefile +10 -13
  3. data/README +56 -385
  4. data/VERSION +1 -1
  5. data/dist-files +26 -27
  6. data/ext/oci8/.document +1 -0
  7. data/ext/oci8/MANIFEST +0 -4
  8. data/ext/oci8/apiwrap.c.tmpl +172 -0
  9. data/ext/oci8/apiwrap.h.tmpl +61 -0
  10. data/ext/oci8/apiwrap.rb +91 -0
  11. data/ext/oci8/apiwrap.yml +1243 -0
  12. data/ext/oci8/attr.c +124 -384
  13. data/ext/oci8/bind.c +472 -164
  14. data/ext/oci8/encoding.c +196 -0
  15. data/ext/oci8/env.c +84 -253
  16. data/ext/oci8/error.c +196 -127
  17. data/ext/oci8/extconf.rb +82 -59
  18. data/ext/oci8/lob.c +710 -370
  19. data/ext/oci8/metadata.c +359 -0
  20. data/ext/oci8/object.c +622 -0
  21. data/ext/oci8/oci8.c +577 -161
  22. data/ext/oci8/oci8.h +354 -258
  23. data/ext/oci8/oci8lib.c +493 -0
  24. data/ext/oci8/ocidatetime.c +473 -0
  25. data/ext/oci8/ocinumber.c +1123 -24
  26. data/ext/oci8/oraconf.rb +72 -106
  27. data/ext/oci8/oradate.c +511 -321
  28. data/ext/oci8/stmt.c +752 -572
  29. data/ext/oci8/win32.c +131 -0
  30. data/ext/oci8/xmldb.c +383 -0
  31. data/lib/.document +2 -0
  32. data/lib/dbd/OCI8.rb +2 -17
  33. data/lib/oci8.rb.in +41 -1622
  34. data/lib/oci8/.document +5 -0
  35. data/lib/oci8/compat.rb +108 -0
  36. data/lib/oci8/datetime.rb +489 -0
  37. data/lib/oci8/encoding-init.rb +40 -0
  38. data/lib/oci8/encoding.yml +537 -0
  39. data/lib/oci8/metadata.rb +2077 -0
  40. data/lib/oci8/object.rb +548 -0
  41. data/lib/oci8/oci8.rb +773 -0
  42. data/lib/oci8/oracle_version.rb +144 -0
  43. data/metaconfig +3 -3
  44. data/ruby-oci8.gemspec +5 -5
  45. data/setup.rb +4 -4
  46. data/test/config.rb +64 -84
  47. data/test/test_all.rb +14 -21
  48. data/test/test_array_dml.rb +317 -0
  49. data/test/test_bind_raw.rb +18 -25
  50. data/test/test_bind_time.rb +78 -91
  51. data/test/test_break.rb +37 -35
  52. data/test/test_clob.rb +33 -89
  53. data/test/test_connstr.rb +5 -4
  54. data/test/test_datetime.rb +469 -0
  55. data/test/test_dbi.rb +99 -60
  56. data/test/test_dbi_clob.rb +3 -8
  57. data/test/test_metadata.rb +65 -51
  58. data/test/test_oci8.rb +151 -55
  59. data/test/test_oracle_version.rb +70 -0
  60. data/test/test_oradate.rb +76 -83
  61. data/test/test_oranumber.rb +405 -71
  62. data/test/test_rowid.rb +6 -11
  63. metadata +31 -32
  64. data/NEWS +0 -420
  65. data/ext/oci8/const.c +0 -165
  66. data/ext/oci8/define.c +0 -53
  67. data/ext/oci8/describe.c +0 -81
  68. data/ext/oci8/descriptor.c +0 -39
  69. data/ext/oci8/handle.c +0 -273
  70. data/ext/oci8/oranumber.c +0 -445
  71. data/ext/oci8/param.c +0 -37
  72. data/ext/oci8/server.c +0 -182
  73. data/ext/oci8/session.c +0 -99
  74. data/ext/oci8/svcctx.c +0 -238
  75. data/ruby-oci8.spec +0 -62
  76. data/support/README +0 -4
  77. data/support/runit/assert.rb +0 -281
  78. data/support/runit/cui/testrunner.rb +0 -101
  79. data/support/runit/error.rb +0 -4
  80. data/support/runit/method_mappable.rb +0 -20
  81. data/support/runit/robserver.rb +0 -25
  82. data/support/runit/setuppable.rb +0 -15
  83. data/support/runit/teardownable.rb +0 -16
  84. data/support/runit/testcase.rb +0 -113
  85. data/support/runit/testfailure.rb +0 -25
  86. data/support/runit/testresult.rb +0 -121
  87. data/support/runit/testsuite.rb +0 -43
  88. data/support/runit/version.rb +0 -3
  89. data/test/test_describe.rb +0 -137
@@ -0,0 +1,493 @@
1
+ /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright (C) 2002-2008 KUBO Takehiro <kubo@jiubao.org>
4
+ */
5
+
6
+ #include "oci8.h"
7
+
8
+ #define DEBUG_CORE_FILE 1
9
+ #ifdef DEBUG_CORE_FILE
10
+ #include <signal.h>
11
+ #endif
12
+
13
+ static oci8_base_class_t oci8_base_class = {
14
+ NULL,
15
+ NULL,
16
+ sizeof(oci8_base_t),
17
+ };
18
+
19
+ ID oci8_id_new;
20
+ ID oci8_id_get;
21
+ ID oci8_id_set;
22
+ ID oci8_id_keys;
23
+ int oci8_in_finalizer = 0;
24
+ VALUE oci8_cOCIHandle;
25
+
26
+ static ID id_oci8_class;
27
+
28
+ static VALUE mOCI8BindType;
29
+ static VALUE cOCI8BindTypeBase;
30
+
31
+ static VALUE oci8_handle_initialize(VALUE self)
32
+ {
33
+ rb_raise(rb_eNameError, "private method `new' called for %s:Class", rb_class2name(CLASS_OF(self)));
34
+ }
35
+
36
+ void oci8_base_free(oci8_base_t *base)
37
+ {
38
+ while (base->children != NULL) {
39
+ oci8_base_free(base->children);
40
+ }
41
+ oci8_unlink_from_parent(base);
42
+ if (base->klass->free != NULL)
43
+ base->klass->free(base);
44
+ if (base->type >= OCI_DTYPE_FIRST)
45
+ OCIDescriptorFree(base->hp.ptr, base->type);
46
+ else if (base->type >= OCI_HTYPE_FIRST)
47
+ OCIHandleFree(base->hp.ptr, base->type);
48
+ base->type = 0;
49
+ base->hp.ptr = NULL;
50
+ }
51
+
52
+ static VALUE oci8_handle_free(VALUE self)
53
+ {
54
+ oci8_base_t *base = DATA_PTR(self);
55
+
56
+ oci8_base_free(base);
57
+ return self;
58
+ }
59
+
60
+ static void oci8_handle_mark(oci8_base_t *base)
61
+ {
62
+ if (base->klass->mark != NULL)
63
+ base->klass->mark(base);
64
+ }
65
+
66
+ static void oci8_handle_cleanup(oci8_base_t *base)
67
+ {
68
+ oci8_base_free(base);
69
+ free(base);
70
+ }
71
+
72
+ static VALUE oci8_s_allocate(VALUE klass)
73
+ {
74
+ oci8_base_t *base;
75
+ const oci8_base_class_t *base_class;
76
+ VALUE superklass;
77
+ VALUE obj;
78
+
79
+ superklass = klass;
80
+ while (!RTEST(rb_ivar_defined(superklass, id_oci8_class))) {
81
+ superklass = RCLASS_SUPER(superklass);
82
+ if (superklass == rb_cObject)
83
+ rb_raise(rb_eRuntimeError, "private method `new' called for %s:Class", rb_class2name(klass));
84
+ }
85
+ obj = rb_ivar_get(superklass, id_oci8_class);
86
+ base_class = DATA_PTR(obj);
87
+
88
+ base = xmalloc(base_class->size);
89
+ memset(base, 0, base_class->size);
90
+
91
+ obj = Data_Wrap_Struct(klass, oci8_handle_mark, oci8_handle_cleanup, base);
92
+ base->self = obj;
93
+ base->klass = base_class;
94
+ base->parent = NULL;
95
+ base->next = base;
96
+ base->prev = base;
97
+ base->children = NULL;
98
+ return obj;
99
+ }
100
+
101
+ static void at_exit_func(VALUE val)
102
+ {
103
+ oci8_in_finalizer = 1;
104
+ }
105
+
106
+ void
107
+ Init_oci8lib()
108
+ {
109
+ VALUE cOCI8;
110
+ VALUE obj;
111
+
112
+ #ifdef RUNTIME_API_CHECK
113
+ Init_oci8_apiwrap();
114
+ if (have_OCIClientVersion) {
115
+ sword major, minor, update, patch, port_update;
116
+ OCIClientVersion(&major, &minor, &update, &patch, &port_update);
117
+ oracle_client_version = ORAVERNUM(major, minor, update, patch, port_update);
118
+ }
119
+ #endif
120
+
121
+ id_oci8_class = rb_intern("__oci8_class__");
122
+ oci8_id_new = rb_intern("new");
123
+ oci8_id_get = rb_intern("get");
124
+ oci8_id_set = rb_intern("set");
125
+ oci8_id_keys = rb_intern("keys");
126
+ rb_set_end_proc(at_exit_func, Qnil);
127
+
128
+ Init_oci8_error();
129
+ Init_oci8_env();
130
+
131
+ /* OCIHandle class */
132
+ oci8_cOCIHandle = rb_define_class("OCIHandle", rb_cObject);
133
+ rb_define_alloc_func(oci8_cOCIHandle, oci8_s_allocate);
134
+ rb_define_method(oci8_cOCIHandle, "initialize", oci8_handle_initialize, 0);
135
+ rb_define_method(oci8_cOCIHandle, "free", oci8_handle_free, 0);
136
+ obj = Data_Wrap_Struct(rb_cObject, 0, 0, &oci8_base_class);
137
+ rb_ivar_set(oci8_cOCIHandle, id_oci8_class, obj);
138
+
139
+ /* OCI8 class */
140
+ cOCI8 = Init_oci8();
141
+ /* OCI8::BindType module */
142
+ mOCI8BindType = rb_define_module_under(cOCI8, "BindType");
143
+ /* OCI8::BindType::Base class */
144
+ cOCI8BindTypeBase = rb_define_class_under(mOCI8BindType, "Base", oci8_cOCIHandle);
145
+
146
+ /* Handle */
147
+ Init_oci8_bind(cOCI8BindTypeBase);
148
+ Init_oci8_stmt(cOCI8);
149
+
150
+ /* Encoding */
151
+ Init_oci8_encoding(cOCI8);
152
+
153
+ /* register allocators */
154
+ Init_oci8_metadata(cOCI8);
155
+ Init_oci8_lob(cOCI8);
156
+
157
+ Init_ora_date();
158
+ Init_oci_number(cOCI8);
159
+ Init_oci_datetime();
160
+ Init_oci_object(cOCI8);
161
+ Init_oci_xmldb();
162
+
163
+ #ifdef USE_WIN32_C
164
+ Init_oci8_win32(cOCI8);
165
+ #endif
166
+
167
+ #ifdef DEBUG_CORE_FILE
168
+ signal(SIGSEGV, SIG_DFL);
169
+ #endif
170
+ }
171
+
172
+ VALUE oci8_define_class(const char *name, oci8_base_class_t *base_class)
173
+ {
174
+ VALUE klass = rb_define_class(name, oci8_cOCIHandle);
175
+ VALUE obj = Data_Wrap_Struct(rb_cObject, 0, 0, base_class);
176
+ rb_ivar_set(klass, id_oci8_class, obj);
177
+ return klass;
178
+ }
179
+
180
+ VALUE oci8_define_class_under(VALUE outer, const char *name, oci8_base_class_t *base_class)
181
+ {
182
+ VALUE klass = rb_define_class_under(outer, name, oci8_cOCIHandle);
183
+ VALUE obj = Data_Wrap_Struct(rb_cObject, 0, 0, base_class);
184
+ rb_ivar_set(klass, id_oci8_class, obj);
185
+ return klass;
186
+ }
187
+
188
+ VALUE oci8_define_bind_class(const char *name, const oci8_bind_class_t *bind_class)
189
+ {
190
+ VALUE klass = rb_define_class_under(mOCI8BindType, name, cOCI8BindTypeBase);
191
+ VALUE obj = Data_Wrap_Struct(rb_cObject, 0, 0, (void*)bind_class);
192
+ rb_ivar_set(klass, id_oci8_class, obj);
193
+ return klass;
194
+ }
195
+
196
+ void oci8_link_to_parent(oci8_base_t *base, oci8_base_t *parent)
197
+ {
198
+ if (base->parent != NULL) {
199
+ oci8_unlink_from_parent(base);
200
+ }
201
+ if (parent->children == NULL) {
202
+ parent->children = base;
203
+ } else {
204
+ base->next = parent->children;
205
+ base->prev = parent->children->prev;
206
+ parent->children->prev->next = base;
207
+ parent->children->prev = base;
208
+ }
209
+ base->parent = parent;
210
+ }
211
+
212
+ void oci8_unlink_from_parent(oci8_base_t *base)
213
+ {
214
+ if (base->parent == NULL) {
215
+ return;
216
+ }
217
+ if (base->next == base) {
218
+ base->parent->children = NULL;
219
+ } else {
220
+ if (base->parent->children == base) {
221
+ base->parent->children = base->next;
222
+ }
223
+ base->next->prev = base->prev;
224
+ base->prev->next = base->next;
225
+ base->next = base;
226
+ base->prev = base;
227
+ }
228
+ base->parent = NULL;
229
+ }
230
+
231
+ #ifdef RUBY_VM
232
+ typedef struct {
233
+ dvoid *hndlp;
234
+ OCIError *errhp;
235
+ } ocibreak_arg_t;
236
+
237
+ static VALUE call_OCIBreak(void *user_data)
238
+ {
239
+ ocibreak_arg_t *arg = (ocibreak_arg_t *)user_data;
240
+ OCIBreak(arg->hndlp, arg->errhp);
241
+ return Qnil;
242
+ }
243
+
244
+ static void oci8_unblock_func(void *user_data)
245
+ {
246
+ oci8_svcctx_t *svcctx = (oci8_svcctx_t *)user_data;
247
+ if (svcctx->base.hp.ptr != NULL) {
248
+ ocibreak_arg_t arg;
249
+ arg.hndlp = svcctx->base.hp.ptr;
250
+ arg.errhp = oci8_errhp;
251
+ rb_thread_blocking_region(call_OCIBreak, &arg, NULL, NULL);
252
+ }
253
+ }
254
+
255
+ /* ruby 1.9 */
256
+ sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, void *data)
257
+ {
258
+ if (svcctx->non_blocking) {
259
+ sword rv;
260
+
261
+ if (!NIL_P(svcctx->executing_thread)) {
262
+ rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
263
+ }
264
+ svcctx->executing_thread = rb_thread_current();
265
+ rv = (sword)rb_thread_blocking_region(func, data, oci8_unblock_func, svcctx);
266
+ svcctx->executing_thread = Qnil;
267
+ if (rv == OCI_ERROR) {
268
+ if (oci8_get_error_code(oci8_errhp) == 1013) {
269
+ rb_raise(eOCIBreak, "Canceled by user request.");
270
+ }
271
+ }
272
+ return rv;
273
+ } else {
274
+ return (sword)func(data);
275
+ }
276
+ }
277
+ #else /* RUBY_VM */
278
+
279
+ /* ruby 1.8 */
280
+ sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, void *data)
281
+ {
282
+ struct timeval tv;
283
+ sword rv;
284
+
285
+ if (!NIL_P(svcctx->executing_thread)) {
286
+ rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
287
+ }
288
+ tv.tv_sec = 0;
289
+ tv.tv_usec = 100000;
290
+ svcctx->executing_thread = rb_thread_current();
291
+ while ((rv = func(data)) == OCI_STILL_EXECUTING) {
292
+ rb_thread_wait_for(tv);
293
+ if (tv.tv_usec < 500000)
294
+ tv.tv_usec <<= 1;
295
+ }
296
+ if (rv == OCI_ERROR) {
297
+ if (oci8_get_error_code(oci8_errhp) == 1013) {
298
+ if (have_OCIReset)
299
+ OCIReset(svcctx->base.hp.ptr, oci8_errhp);
300
+ svcctx->executing_thread = Qnil;
301
+ rb_raise(eOCIBreak, "Canceled by user request.");
302
+ }
303
+ }
304
+ svcctx->executing_thread = Qnil;
305
+ return rv;
306
+ }
307
+ #endif /* RUBY_VM */
308
+
309
+ typedef struct {
310
+ oci8_svcctx_t *svcctx;
311
+ const char *sql_text;
312
+ ub4 num_define_vars;
313
+ oci8_exec_sql_var_t *define_vars;
314
+ ub4 num_bind_vars;
315
+ oci8_exec_sql_var_t *bind_vars;
316
+ int raise_on_error;
317
+ OCIStmt *stmtp;
318
+ } cb_arg_t;
319
+
320
+ static VALUE exec_sql(cb_arg_t *arg);
321
+ static VALUE ensure_func(cb_arg_t *arg);
322
+
323
+ /*
324
+ * utility function to execute a single SQL statement
325
+ */
326
+ sword oci8_exec_sql(oci8_svcctx_t *svcctx, const char *sql_text, ub4 num_define_vars, oci8_exec_sql_var_t *define_vars, ub4 num_bind_vars, oci8_exec_sql_var_t *bind_vars, int raise_on_error)
327
+ {
328
+ cb_arg_t arg;
329
+
330
+ oci8_check_pid_consistency(svcctx);
331
+ arg.svcctx = svcctx;
332
+ arg.sql_text = sql_text;
333
+ arg.num_define_vars = num_define_vars;
334
+ arg.define_vars = define_vars;
335
+ arg.num_bind_vars = num_bind_vars;
336
+ arg.bind_vars = bind_vars;
337
+ arg.raise_on_error = raise_on_error;
338
+ arg.stmtp = NULL;
339
+ return (sword)rb_ensure(exec_sql, (VALUE)&arg, ensure_func, (VALUE)&arg);
340
+ }
341
+
342
+ static VALUE exec_sql(cb_arg_t *arg)
343
+ {
344
+ ub4 pos;
345
+ sword rv;
346
+
347
+ rv = OCIHandleAlloc(oci8_envhp, (dvoid*)&arg->stmtp, OCI_HTYPE_STMT, 0, NULL);
348
+ if (rv != OCI_SUCCESS) {
349
+ oci8_env_raise(oci8_envhp, rv);
350
+ }
351
+ oci_lc(OCIStmtPrepare(arg->stmtp, oci8_errhp, (text*)arg->sql_text,
352
+ strlen(arg->sql_text), OCI_NTV_SYNTAX, OCI_DEFAULT));
353
+ for (pos = 0; pos < arg->num_define_vars; pos++) {
354
+ arg->define_vars[pos].hp = NULL;
355
+ oci_lc(OCIDefineByPos(arg->stmtp, (OCIDefine**)&arg->define_vars[pos].hp,
356
+ oci8_errhp, pos + 1, arg->define_vars[pos].valuep,
357
+ arg->define_vars[pos].value_sz,
358
+ arg->define_vars[pos].dty, arg->define_vars[pos].indp,
359
+ arg->define_vars[pos].alenp, NULL, OCI_DEFAULT));
360
+ }
361
+ for (pos = 0; pos < arg->num_bind_vars; pos++) {
362
+ arg->bind_vars[pos].hp = NULL;
363
+ oci_lc(OCIBindByPos(arg->stmtp, (OCIBind**)&arg->bind_vars[pos].hp,
364
+ oci8_errhp, pos + 1, arg->bind_vars[pos].valuep,
365
+ arg->bind_vars[pos].value_sz, arg->bind_vars[pos].dty,
366
+ arg->bind_vars[pos].indp, arg->bind_vars[pos].alenp,
367
+ NULL, 0, NULL, OCI_DEFAULT));
368
+ }
369
+ rv = OCIStmtExecute_nb(arg->svcctx, arg->svcctx->base.hp.svc, arg->stmtp, oci8_errhp, 1, 0, NULL, NULL, OCI_DEFAULT);
370
+ if (rv == OCI_ERROR) {
371
+ if (oci8_get_error_code(oci8_errhp) == 1000) {
372
+ /* run GC to close unreferred cursors
373
+ * when ORA-01000 (maximum open cursors exceeded).
374
+ */
375
+ rb_gc();
376
+ rv = OCIStmtExecute_nb(arg->svcctx, arg->svcctx->base.hp.svc, arg->stmtp, oci8_errhp, 1, 0, NULL, NULL, OCI_DEFAULT);
377
+ }
378
+ }
379
+ if (arg->raise_on_error) {
380
+ oci_lc(rv);
381
+ }
382
+ return (VALUE)rv;
383
+ }
384
+
385
+ static VALUE ensure_func(cb_arg_t *arg)
386
+ {
387
+ if (arg->stmtp != NULL) {
388
+ OCIHandleFree(arg->stmtp, OCI_HTYPE_STMT);
389
+ }
390
+ return Qnil;
391
+ }
392
+
393
+ #if defined RUNTIME_API_CHECK
394
+
395
+ #ifndef _WIN32
396
+ #include <dlfcn.h>
397
+ #endif
398
+
399
+ void *oci8_find_symbol(const char *symbol_name)
400
+ {
401
+ #if defined _WIN32
402
+ /* Windows */
403
+ static HMODULE hModule = NULL;
404
+
405
+ if (hModule == NULL) {
406
+ hModule = LoadLibrary("OCI.DLL");
407
+ if (hModule == NULL) {
408
+ char message[512];
409
+ int error = GetLastError();
410
+ char *p;
411
+
412
+ memset(message, 0, sizeof(message));
413
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), message, sizeof(message), NULL);
414
+ for (p = message; *p; p++) {
415
+ if (*p == '\n' || *p == '\r')
416
+ *p = ' ';
417
+ }
418
+ rb_raise(rb_eLoadError, "OCI.DLL: %d(%s)", error, message);
419
+ }
420
+ }
421
+ return GetProcAddress(hModule, symbol_name);
422
+ #else
423
+ /* UNIX */
424
+ static void *handle = NULL;
425
+
426
+ if (handle == NULL) {
427
+ static const char * const sonames[] = {
428
+ #if defined(__CYGWIN__)
429
+ /* Windows(Cygwin) */
430
+ "OCI.DLL",
431
+ #elif defined(_AIX)
432
+ /* AIX */
433
+ "libclntsh.a(shr.o)",
434
+ #elif defined(__hppa)
435
+ /* HP-UX(PA-RISC) */
436
+ "libclntsh.sl.11.1",
437
+ "libclntsh.sl.10.1",
438
+ "libclntsh.sl.9.0",
439
+ "libclntsh.sl.8.0",
440
+ #elif defined(__APPLE__)
441
+ /* Mac OS X */
442
+ "libclntsh.dylib.11.1",
443
+ "libclntsh.dylib.10.1",
444
+ #else
445
+ /* Linux, Solaris and HP-UX(IA64) */
446
+ "libclntsh.so.11.1",
447
+ "libclntsh.so.10.1",
448
+ "libclntsh.so.9.0",
449
+ "libclntsh.so.8.0",
450
+ #endif
451
+ };
452
+ #define NUM_SONAMES (sizeof(sonames)/sizeof(sonames[0]))
453
+ size_t idx;
454
+ VALUE err = rb_ary_new();
455
+
456
+ #ifdef _AIX
457
+ #define DLOPEN_FLAG (RTLD_LAZY|RTLD_GLOBAL|RTLD_MEMBER)
458
+ #else
459
+ #define DLOPEN_FLAG (RTLD_LAZY|RTLD_GLOBAL)
460
+ #endif
461
+ for (idx = 0; idx < NUM_SONAMES; idx++) {
462
+ handle = dlopen(sonames[idx], DLOPEN_FLAG);
463
+ if (handle != NULL) {
464
+ break;
465
+ }
466
+ rb_ary_push(err, rb_locale_str_new_cstr(dlerror()));
467
+ }
468
+ if (handle == NULL) {
469
+ VALUE msg;
470
+
471
+ msg = rb_str_buf_new(NUM_SONAMES * 50);
472
+ for (idx = 0; idx < NUM_SONAMES; idx++) {
473
+ const char *errmsg = RSTRING_PTR(RARRAY_PTR(err)[idx]);
474
+ if (idx != 0) {
475
+ rb_str_buf_cat2(msg, " ");
476
+ }
477
+ if (strstr(errmsg, sonames[idx]) == NULL) {
478
+ /* prepend "soname: " if soname is not found in
479
+ * the error message.
480
+ */
481
+ rb_str_buf_cat2(msg, sonames[idx]);
482
+ rb_str_buf_cat2(msg, ": ");
483
+ }
484
+ rb_str_buf_append(msg, RARRAY_PTR(err)[idx]);
485
+ rb_str_buf_cat2(msg, ";");
486
+ }
487
+ rb_exc_raise(rb_exc_new3(rb_eLoadError, msg));
488
+ }
489
+ }
490
+ return dlsym(handle, symbol_name);
491
+ #endif /* defined _WIN32 */
492
+ }
493
+ #endif /* RUNTIME_API_CHECK */