ruby-oci8 2.2.3 → 2.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/ChangeLog +427 -0
  3. data/NEWS +335 -42
  4. data/README.md +20 -9
  5. data/dist-files +9 -3
  6. data/docs/bind-array-to-in_cond.md +2 -2
  7. data/docs/conflicts-local-connections-and-processes.md +7 -4
  8. data/docs/hanging-after-inactivity.md +63 -0
  9. data/docs/install-binary-package.md +15 -11
  10. data/docs/install-full-client.md +18 -21
  11. data/docs/install-instant-client.md +45 -27
  12. data/docs/install-on-osx.md +31 -120
  13. data/docs/ldap-auth-and-function-interposition.md +123 -0
  14. data/docs/number-type-mapping.md +79 -0
  15. data/docs/platform-specific-issues.md +17 -50
  16. data/docs/report-installation-issue.md +3 -0
  17. data/docs/timeout-parameters.md +3 -0
  18. data/ext/oci8/apiwrap.c.tmpl +2 -5
  19. data/ext/oci8/apiwrap.rb +6 -1
  20. data/ext/oci8/apiwrap.yml +34 -22
  21. data/ext/oci8/attr.c +4 -2
  22. data/ext/oci8/bind.c +366 -6
  23. data/ext/oci8/connection_pool.c +3 -3
  24. data/ext/oci8/encoding.c +5 -5
  25. data/ext/oci8/env.c +8 -2
  26. data/ext/oci8/error.c +24 -16
  27. data/ext/oci8/extconf.rb +8 -4
  28. data/ext/oci8/hook_funcs.c +274 -61
  29. data/ext/oci8/lob.c +31 -75
  30. data/ext/oci8/metadata.c +2 -2
  31. data/ext/oci8/object.c +72 -27
  32. data/ext/oci8/oci8.c +45 -132
  33. data/ext/oci8/oci8.h +32 -88
  34. data/ext/oci8/oci8lib.c +178 -38
  35. data/ext/oci8/ocihandle.c +37 -37
  36. data/ext/oci8/ocinumber.c +23 -18
  37. data/ext/oci8/oraconf.rb +158 -339
  38. data/ext/oci8/oradate.c +19 -19
  39. data/ext/oci8/plthook.h +10 -0
  40. data/ext/oci8/plthook_elf.c +433 -268
  41. data/ext/oci8/plthook_osx.c +40 -9
  42. data/ext/oci8/plthook_win32.c +9 -0
  43. data/ext/oci8/stmt.c +52 -17
  44. data/ext/oci8/win32.c +4 -22
  45. data/lib/oci8/bindtype.rb +1 -15
  46. data/lib/oci8/check_load_error.rb +57 -10
  47. data/lib/oci8/cursor.rb +57 -25
  48. data/lib/oci8/metadata.rb +9 -1
  49. data/lib/oci8/object.rb +10 -0
  50. data/lib/oci8/oci8.rb +33 -28
  51. data/lib/oci8/oracle_version.rb +11 -1
  52. data/lib/oci8/properties.rb +22 -0
  53. data/lib/oci8/version.rb +1 -1
  54. data/lib/oci8.rb +48 -4
  55. data/lib/ruby-oci8.rb +0 -3
  56. data/pre-distclean.rb +1 -3
  57. data/ruby-oci8.gemspec +3 -8
  58. data/setup.rb +11 -2
  59. data/test/README.md +37 -0
  60. data/test/config.rb +1 -1
  61. data/test/setup_test_object.sql +21 -13
  62. data/test/setup_test_package.sql +59 -0
  63. data/test/test_all.rb +2 -0
  64. data/test/test_bind_boolean.rb +99 -0
  65. data/test/test_bind_integer.rb +47 -0
  66. data/test/test_break.rb +11 -9
  67. data/test/test_clob.rb +4 -16
  68. data/test/test_connstr.rb +29 -13
  69. data/test/test_datetime.rb +8 -3
  70. data/test/test_object.rb +27 -9
  71. data/test/test_oci8.rb +170 -46
  72. data/test/test_oranumber.rb +12 -6
  73. data/test/test_package_type.rb +15 -3
  74. data/test/test_properties.rb +17 -0
  75. metadata +40 -54
  76. data/docs/osx-install-dev-tools.png +0 -0
  77. data/test/README +0 -42
@@ -73,10 +73,10 @@ struct plthook {
73
73
  };
74
74
 
75
75
  static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *mh);
76
- static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, int addrdiff);
76
+ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, ptrdiff_t addrdiff);
77
77
 
78
78
  static void set_errmsg(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2)));
79
- static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, int seg_offset, struct segment_command_ **segments);
79
+ static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, uint64_t seg_offset, struct segment_command_ **segments);
80
80
 
81
81
  static uint64_t uleb128(const uint8_t **p)
82
82
  {
@@ -142,6 +142,37 @@ int plthook_open(plthook_t **plthook_out, const char *filename)
142
142
  return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
143
143
  }
144
144
 
145
+ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
146
+ {
147
+ int flags[] = {
148
+ RTLD_LAZY | RTLD_NOLOAD,
149
+ RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST,
150
+ };
151
+ size_t flag_idx;
152
+ #define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
153
+
154
+ if (hndl == NULL) {
155
+ set_errmsg("NULL handle");
156
+ return PLTHOOK_FILE_NOT_FOUND;
157
+ }
158
+ for (flag_idx = 0; flag_idx < NUM_FLAGS; flag_idx++) {
159
+ const char *image_name = NULL;
160
+ uint32_t idx = 0;
161
+
162
+ do {
163
+ void *handle = dlopen(image_name, flags[flag_idx]);
164
+ if (handle != NULL) {
165
+ dlclose(handle);
166
+ if (handle == hndl) {
167
+ return plthook_open_real(plthook_out, _dyld_get_image_header(idx));
168
+ }
169
+ }
170
+ } while ((image_name = _dyld_get_image_name(++idx)) != NULL);
171
+ }
172
+ set_errmsg("Cannot find the image correspond to handle %p", hndl);
173
+ return PLTHOOK_FILE_NOT_FOUND;
174
+ }
175
+
145
176
  int plthook_open_by_address(plthook_t **plthook_out, void *address)
146
177
  {
147
178
  Dl_info dlinfo;
@@ -165,8 +196,8 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
165
196
  struct segment_command_ *segments[NUM_SEGMENTS];
166
197
  int segment_idx = 0;
167
198
  unsigned int nbind;
168
- int addrdiff = 0;
169
- int i;
199
+ ptrdiff_t addrdiff = 0;
200
+ uint32_t i;
170
201
 
171
202
  memset(segments, 0, sizeof(segments));
172
203
  #ifdef __LP64__
@@ -289,14 +320,14 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
289
320
  return 0;
290
321
  }
291
322
 
292
- static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, int addrdiff)
323
+ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint32_t lazy_bind_off, uint32_t lazy_bind_size, struct segment_command_ **segments, ptrdiff_t addrdiff)
293
324
  {
294
325
  const uint8_t *ptr = base + lazy_bind_off + addrdiff;
295
326
  const uint8_t *end = ptr + lazy_bind_size;
296
327
  const char *sym_name;
297
328
  int seg_index = 0;
298
329
  uint64_t seg_offset = 0;
299
- int count, skip;
330
+ uint64_t count, skip;
300
331
  unsigned int idx;
301
332
  DEBUG_BIND("get_bind_addr(%p, 0x%x, 0x%x", base, lazy_bind_off, lazy_bind_size);
302
333
  for (idx = 0; segments[idx] != NULL; idx++) {
@@ -310,7 +341,7 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
310
341
  uint8_t imm = *ptr & BIND_IMMEDIATE_MASK;
311
342
  uint64_t ulebval;
312
343
  int64_t slebval;
313
- int i;
344
+ uint64_t i;
314
345
 
315
346
  DEBUG_BIND("0x%02x: ", *ptr);
316
347
  ptr++;
@@ -379,10 +410,10 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
379
410
  return idx;
380
411
  }
381
412
 
382
- static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, int seg_offset, struct segment_command_ **segments)
413
+ static void set_bind_addr(unsigned int *idx, plthook_t *plthook, const uint8_t *base, const char *sym_name, int seg_index, uint64_t seg_offset, struct segment_command_ **segments)
383
414
  {
384
415
  if (plthook != NULL) {
385
- uint32_t vmaddr = segments[seg_index]->vmaddr;
416
+ uint64_t vmaddr = segments[seg_index]->vmaddr;
386
417
  plthook->entries[*idx].name = sym_name;
387
418
  plthook->entries[*idx].addr = (void**)(base + vmaddr + seg_offset);
388
419
  }
@@ -93,6 +93,15 @@ int plthook_open(plthook_t **plthook_out, const char *filename)
93
93
  return plthook_open_real(plthook_out, hMod);
94
94
  }
95
95
 
96
+ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
97
+ {
98
+ if (hndl == NULL) {
99
+ set_errmsg("NULL handle");
100
+ return PLTHOOK_FILE_NOT_FOUND;
101
+ }
102
+ return plthook_open_real(plthook_out, (HMODULE)hndl);
103
+ }
104
+
96
105
  int plthook_open_by_address(plthook_t **plthook_out, void *address)
97
106
  {
98
107
  HMODULE hMod;
data/ext/oci8/stmt.c CHANGED
@@ -15,7 +15,8 @@ static VALUE cOCIStmt;
15
15
  typedef struct {
16
16
  oci8_base_t base;
17
17
  VALUE svc;
18
- int use_stmt_release;
18
+ char use_stmt_release;
19
+ char end_of_fetch;
19
20
  } oci8_stmt_t;
20
21
 
21
22
  static void oci8_stmt_mark(oci8_base_t *base)
@@ -77,7 +78,7 @@ static VALUE oci8_stmt_initialize(VALUE self, VALUE svc, VALUE sql)
77
78
  if (!NIL_P(sql)) {
78
79
  OCI8SafeStringValue(sql);
79
80
 
80
- rv = OCIStmtPrepare2(svcctx->base.hp.svc, &stmt->base.hp.stmt, oci8_errhp, RSTRING_ORATEXT(sql), RSTRING_LEN(sql), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT);
81
+ rv = OCIStmtPrepare2(svcctx->base.hp.svc, &stmt->base.hp.stmt, oci8_errhp, RSTRING_ORATEXT(sql), RSTRING_LENINT(sql), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT);
81
82
  if (IS_OCI_ERROR(rv)) {
82
83
  chker2(rv, &svcctx->base);
83
84
  }
@@ -111,12 +112,24 @@ static VALUE oci8_define_by_pos(VALUE self, VALUE vposition, VALUE vbindobj)
111
112
  oci8_bind_t *obind = TO_BIND(vbindobj);
112
113
  const oci8_bind_data_type_t *data_type;
113
114
  sword status;
115
+ void *valuep;
116
+ void *indp;
117
+ ub4 mode;
114
118
 
115
119
  if (obind->base.hp.dfn != NULL) {
116
120
  oci8_base_free(&obind->base); /* TODO: OK? */
117
121
  }
118
122
  data_type = (const oci8_bind_data_type_t *)obind->base.data_type;
119
- status = OCIDefineByPos(stmt->base.hp.stmt, &obind->base.hp.dfn, oci8_errhp, position, obind->valuep, obind->value_sz, data_type->dty, NIL_P(obind->tdo) ? obind->u.inds : NULL, NULL, 0, OCI_DEFAULT);
123
+ if (obind->value_sz != SB4MAXVAL) {
124
+ valuep = obind->valuep;
125
+ indp = NIL_P(obind->tdo) ? obind->u.inds : NULL;
126
+ mode = OCI_DEFAULT;
127
+ } else {
128
+ valuep = NULL;
129
+ indp = NULL;
130
+ mode = OCI_DYNAMIC_FETCH;
131
+ }
132
+ status = OCIDefineByPos(stmt->base.hp.stmt, &obind->base.hp.dfn, oci8_errhp, position, valuep, obind->value_sz, data_type->dty, indp, NULL, 0, mode);
120
133
  if (status != OCI_SUCCESS) {
121
134
  chker3(status, &stmt->base, stmt->base.hp.ptr);
122
135
  }
@@ -151,7 +164,9 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
151
164
  oci8_bind_t *obind;
152
165
  const oci8_bind_data_type_t *data_type;
153
166
  sword status;
167
+ void *valuep;
154
168
  void *indp;
169
+ ub4 mode;
155
170
 
156
171
  if (NIL_P(vplaceholder)) { /* 1 */
157
172
  placeholder_ptr = NULL;
@@ -163,10 +178,10 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
163
178
  */
164
179
  VALUE symval = rb_sym2str(vplaceholder);
165
180
  const char *symname = RSTRING_PTR(symval);
166
- size_t len = RSTRING_LEN(symval);
181
+ ub4 len = RSTRING_LENINT(symval);
167
182
  #else
168
183
  const char *symname = rb_id2name(SYM2ID(vplaceholder));
169
- size_t len = strlen(symname);
184
+ ub4 len = (ub4)strlen(symname);
170
185
  #endif
171
186
  placeholder_ptr = ALLOCA_N(char, len + 1);
172
187
  placeholder_len = len + 1;
@@ -177,7 +192,7 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
177
192
  } else {
178
193
  OCI8StringValue(vplaceholder);
179
194
  placeholder_ptr = RSTRING_PTR(vplaceholder);
180
- placeholder_len = RSTRING_LEN(vplaceholder);
195
+ placeholder_len = RSTRING_LENINT(vplaceholder);
181
196
  }
182
197
  obind = TO_BIND(vbindobj); /* 2 */
183
198
  if (obind->base.hp.bnd != NULL) {
@@ -185,11 +200,19 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
185
200
  }
186
201
  data_type = (const oci8_bind_data_type_t *)obind->base.data_type;
187
202
 
188
- indp = NIL_P(obind->tdo) ? obind->u.inds : NULL;
203
+ if (obind->value_sz != SB4MAXVAL) {
204
+ valuep = obind->valuep;
205
+ indp = NIL_P(obind->tdo) ? obind->u.inds : NULL;
206
+ mode = OCI_DEFAULT;
207
+ } else {
208
+ valuep = NULL;
209
+ indp = NULL;
210
+ mode = OCI_DATA_AT_EXEC;
211
+ }
189
212
  if (placeholder_ptr == (char*)-1) {
190
- status = OCIBindByPos(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, position, obind->valuep, obind->value_sz, data_type->dty, indp, NULL, 0, 0, 0, OCI_DEFAULT);
213
+ status = OCIBindByPos(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, position, valuep, obind->value_sz, data_type->dty, indp, NULL, 0, 0, 0, mode);
191
214
  } else {
192
- status = OCIBindByName(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, TO_ORATEXT(placeholder_ptr), placeholder_len, obind->valuep, obind->value_sz, data_type->dty, indp, NULL, 0, 0, 0, OCI_DEFAULT);
215
+ status = OCIBindByName(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, TO_ORATEXT(placeholder_ptr), placeholder_len, valuep, obind->value_sz, data_type->dty, indp, NULL, 0, 0, 0, mode);
193
216
  }
194
217
  if (status != OCI_SUCCESS) {
195
218
  chker3(status, &stmt->base, stmt->base.hp.stmt);
@@ -238,6 +261,7 @@ static VALUE oci8_stmt_execute(VALUE self, VALUE iteration_count)
238
261
  oci8_stmt_t *stmt = TO_STMT(self);
239
262
  oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
240
263
 
264
+ stmt->end_of_fetch = 0;
241
265
  chker3(oci8_call_stmt_execute(svcctx, stmt, NUM2UINT(iteration_count),
242
266
  svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT),
243
267
  &stmt->base, stmt->base.hp.stmt);
@@ -245,7 +269,7 @@ static VALUE oci8_stmt_execute(VALUE self, VALUE iteration_count)
245
269
  }
246
270
 
247
271
  /*
248
- * @overload __fetch(connection)
272
+ * @overload __fetch(connection, max_rows)
249
273
  *
250
274
  * Fetches one row and set the result to <code>@define_handles</code>.
251
275
  * This is called by private methods of OCI8::Cursor.
@@ -255,13 +279,18 @@ static VALUE oci8_stmt_execute(VALUE self, VALUE iteration_count)
255
279
  *
256
280
  * @private
257
281
  */
258
- static VALUE oci8_stmt_fetch(VALUE self, VALUE svc)
282
+ static VALUE oci8_stmt_fetch(VALUE self, VALUE svc, VALUE max_rows)
259
283
  {
260
284
  oci8_stmt_t *stmt = TO_STMT(self);
261
285
  oci8_svcctx_t *svcctx = oci8_get_svcctx(svc);
262
286
  sword rv;
263
287
  oci8_bind_t *obind;
264
288
  const oci8_bind_data_type_t *data_type;
289
+ ub4 nrows = NUM2UINT(max_rows);
290
+
291
+ if (stmt->end_of_fetch) {
292
+ return Qnil;
293
+ }
265
294
 
266
295
  if (stmt->base.children != NULL) {
267
296
  obind = (oci8_bind_t *)stmt->base.children;
@@ -271,16 +300,22 @@ static VALUE oci8_stmt_fetch(VALUE self, VALUE svc)
271
300
  if (data_type->pre_fetch_hook != NULL) {
272
301
  data_type->pre_fetch_hook(obind, stmt->svc);
273
302
  }
303
+ if (nrows > 1 && nrows != obind->maxar_sz) {
304
+ rb_raise(rb_eRuntimeError, "fetch size (%u) != define-handle size %u", nrows, obind->maxar_sz);
305
+ }
274
306
  }
275
307
  obind = (oci8_bind_t *)obind->base.next;
276
308
  } while (obind != (oci8_bind_t*)stmt->base.children);
277
309
  }
278
- rv = OCIStmtFetch_nb(svcctx, stmt->base.hp.stmt, oci8_errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
310
+ rv = OCIStmtFetch_nb(svcctx, stmt->base.hp.stmt, oci8_errhp, nrows, OCI_FETCH_NEXT, OCI_DEFAULT);
279
311
  if (rv == OCI_NO_DATA) {
280
- return Qfalse;
312
+ stmt->end_of_fetch = 1;
313
+ } else {
314
+ chker3(rv, &svcctx->base, stmt->base.hp.stmt);
281
315
  }
282
- chker3(rv, &svcctx->base, stmt->base.hp.stmt);
283
- return Qtrue;
316
+ chker2(OCIAttrGet(stmt->base.hp.stmt, OCI_HTYPE_STMT, &nrows, 0, OCI_ATTR_ROWS_FETCHED, oci8_errhp),
317
+ &svcctx->base);
318
+ return nrows ? UINT2NUM(nrows) : Qnil;
284
319
  }
285
320
 
286
321
  /*
@@ -288,7 +323,7 @@ static VALUE oci8_stmt_fetch(VALUE self, VALUE svc)
288
323
  *
289
324
  * Returns the definition of column specified by <i>pos</i>
290
325
  *
291
- * @param [Fixnum] pos Column position which starts from one
326
+ * @param [Integer] pos Column position which starts from one
292
327
  * @return [OCI8::Metadata::Base]
293
328
  *
294
329
  * @private
@@ -405,7 +440,7 @@ void Init_oci8_stmt(VALUE cOCI8)
405
440
  rb_define_private_method(cOCIStmt, "__define", oci8_define_by_pos, 2);
406
441
  rb_define_private_method(cOCIStmt, "__bind", oci8_bind, 2);
407
442
  rb_define_private_method(cOCIStmt, "__execute", oci8_stmt_execute, 1);
408
- rb_define_private_method(cOCIStmt, "__fetch", oci8_stmt_fetch, 1);
443
+ rb_define_private_method(cOCIStmt, "__fetch", oci8_stmt_fetch, 2);
409
444
  rb_define_private_method(cOCIStmt, "__paramGet", oci8_stmt_get_param, 1);
410
445
  rb_define_method(cOCIStmt, "rowid", oci8_stmt_get_rowid, 0);
411
446
 
data/ext/oci8/win32.c CHANGED
@@ -21,33 +21,14 @@
21
21
  * @private
22
22
  */
23
23
 
24
- NORETURN(static void raise_error(void));
25
-
26
- static void raise_error(void)
27
- {
28
- char msg[1024];
29
- int err = GetLastError();
30
- char *p;
31
-
32
- sprintf(msg, "%d: ", err);
33
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
34
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
35
- msg + strlen(msg), sizeof(msg) - strlen(msg), NULL);
36
- for (p = msg; *p != '\0'; p++) {
37
- if (*p == '\n' || *p == '\r') {
38
- *p = ' ';
39
- }
40
- }
41
- rb_raise(rb_eRuntimeError, "%s", msg);
42
- }
43
-
44
24
  typedef struct {
45
25
  HKEY hKey;
46
26
  HKEY hSubKey;
47
27
  } enum_homes_arg_t;
48
28
 
49
- static VALUE enum_homes_real(enum_homes_arg_t *arg)
29
+ static VALUE enum_homes_real(VALUE varg)
50
30
  {
31
+ enum_homes_arg_t *arg = (enum_homes_arg_t *)varg;
51
32
  LONG rv;
52
33
  DWORD type;
53
34
  DWORD idx;
@@ -97,8 +78,9 @@ static VALUE enum_homes_real(enum_homes_arg_t *arg)
97
78
  return Qnil;
98
79
  }
99
80
 
100
- static VALUE enum_homes_ensure(enum_homes_arg_t *arg)
81
+ static VALUE enum_homes_ensure(VALUE varg)
101
82
  {
83
+ enum_homes_arg_t *arg = (enum_homes_arg_t *)varg;
102
84
  if (arg->hKey != NULL) {
103
85
  RegCloseKey(arg->hKey);
104
86
  arg->hKey = NULL;
data/lib/oci8/bindtype.rb CHANGED
@@ -49,7 +49,7 @@ class OCI8
49
49
 
50
50
  class BasicNumberType < OCI8::BindType::OraNumber
51
51
  def get()
52
- (val = super()) && (val.has_decimal_part? ? val.to_f : val.to_i)
52
+ (val = super()) && (val.has_fractional_part? ? val.to_f : val.to_i)
53
53
  end
54
54
  end
55
55
 
@@ -172,20 +172,6 @@ class OCI8
172
172
  end
173
173
  end
174
174
 
175
- class Long < OCI8::BindType::String
176
- def self.create(con, val, param, max_array_size)
177
- param = {:length => con.long_read_len, :char_semantics => true}
178
- super(con, val, param, max_array_size)
179
- end
180
- end
181
-
182
- class LongRaw < OCI8::BindType::RAW
183
- def self.create(con, val, param, max_array_size)
184
- param = {:length => con.long_read_len, :char_semantics => false}
185
- self.new(con, val, param, max_array_size)
186
- end
187
- end
188
-
189
175
  class CLOB
190
176
  def self.create(con, val, param, max_array_size)
191
177
  if param.is_a? OCI8::Metadata::Base and param.charset_form == :nchar
@@ -4,32 +4,79 @@ class OCI8
4
4
  module Util
5
5
 
6
6
  case RUBY_PLATFORM
7
- when /mswin32|cygwin|mingw32|bccwin32/
7
+ when /mswin32|cygwin|mingw/
8
+
9
+ require 'fiddle/import'
10
+ require 'fiddle/types'
11
+
12
+ extend Fiddle::Importer
13
+ dlload 'kernel32.dll'
14
+ include Fiddle::BasicTypes
15
+ include Fiddle::Win32Types
16
+
17
+ typealias "HANDLE", "void*"
18
+ typealias "HMODULE", "void*"
19
+ extern "DWORD GetModuleFileNameA(HMODULE, LPSTR, DWORD)"
20
+ extern "UINT GetSystemDirectoryA(LPCSTR, UINT)"
21
+ extern "UINT GetWindowsDirectoryA(LPCSTR, UINT)"
22
+ extern "HMODULE LoadLibraryExA(LPCSTR, HANDLE, DWORD)"
23
+ extern "BOOL FreeLibrary(HMODULE)"
8
24
 
9
- require 'Win32API'
10
25
  MAX_PATH = 260
11
- GetModuleFileNameA = Win32API.new('kernel32', 'GetModuleFileNameA', 'PPL', 'L')
12
- GetSystemDirectoryA = Win32API.new('kernel32', 'GetSystemDirectoryA', 'PL', 'L')
13
- GetWindowsDirectoryA = Win32API.new('kernel32', 'GetWindowsDirectoryA', 'PL', 'L')
26
+ DONT_RESOLVE_DLL_REFERENCES = 0x00000001
14
27
 
15
28
  def self.check_os_specific_load_error(exc)
16
29
  case exc.message
17
- when /^193: / # "193: %1 is not a valid Win32 application." in English
30
+ when /^OCI\.DLL: 193\(/, /^193: / # "OCI.DLL: 193(%1 is not a valid Win32 application.)" in English
18
31
  check_win32_pe_arch(exc.message.split(' - ')[1], "ruby-oci8")
19
32
  dll_load_path_list.each do |path|
20
33
  check_win32_pe_arch(File.join(path, '\OCI.DLL'), "Oracle client")
21
34
  end
35
+ when /^OCI.DLL: 126\(/, /^126: / # "OCI.DLL: 126(The specified module could not be found.)" in English
36
+ oci_dll_files = dll_load_path_list.inject([]) do |files, path|
37
+ file = File.join(path, '\OCI.DLL')
38
+ files << file if File.exist?(file)
39
+ files
40
+ end
41
+ if oci_dll_files.empty?
42
+ raise LoadError, "Cannot find OCI.DLL in PATH."
43
+ end
44
+ if oci_dll_files.none? {|file| open(file, 'rb') {true} rescue false}
45
+ raise LoadError, "OCI.DLL in PATH isn't readable."
46
+ end
47
+ first_error = nil
48
+ oci_dll_files.each do |file|
49
+ begin
50
+ check_win32_pe_arch(file, "Oracle client")
51
+ valid_arch = true
52
+ rescue LoadError
53
+ first_error ||= $!
54
+ valid_arch = false
55
+ end
56
+ if valid_arch
57
+ handle = LoadLibraryExA(file, nil, DONT_RESOLVE_DLL_REFERENCES)
58
+ unless handle.null?
59
+ FreeLibrary(handle)
60
+ raise LoadError, <<EOS
61
+ Cannot find DLLs depended by #{file}.
62
+ See http://www.rubydoc.info/github/kubo/ruby-oci8/file/docs/install-instant-client.md#Windows
63
+ EOS
64
+ end
65
+ break
66
+ end
67
+ end
68
+ raise first_error if first_error
22
69
  end
23
70
  end # self.check_os_specific_load_error
24
71
 
25
72
  def self.dll_load_path_list
26
73
  buf = "\0" * MAX_PATH
27
74
  paths = []
28
- paths << buf[0, GetModuleFileNameA.call(nil, buf, MAX_PATH)].force_encoding("locale").gsub(/\\[^\\]*$/, '')
29
- paths << buf[0, GetSystemDirectoryA.call(buf, MAX_PATH)].force_encoding("locale")
30
- paths << buf[0, GetWindowsDirectoryA.call(buf, MAX_PATH)].force_encoding("locale")
75
+ paths << buf[0, GetModuleFileNameA(nil, buf, MAX_PATH)].force_encoding("locale").gsub(/\\[^\\]*$/, '')
76
+ paths << buf[0, GetSystemDirectoryA(buf, MAX_PATH)].force_encoding("locale")
77
+ paths << buf[0, GetWindowsDirectoryA(buf, MAX_PATH)].force_encoding("locale")
31
78
  paths << "."
32
- paths + ENV['PATH'].split(';')
79
+ paths + (ENV['PATH'].split(';').reject {|path| path.empty?})
33
80
  end # self.dll_load_path_list
34
81
 
35
82
  def self.check_win32_pe_arch(filename, package)
data/lib/oci8/cursor.rb CHANGED
@@ -25,7 +25,11 @@ class OCI8
25
25
  @names = nil
26
26
  @con = conn
27
27
  @max_array_size = nil
28
+ @fetch_array_size = nil
29
+ @rowbuf_size = 0
30
+ @rowbuf_index = 0
28
31
  __initialize(conn, sql) # Initialize the internal C structure.
32
+ self.prefetch_rows = conn.instance_variable_get(:@prefetch_rows)
29
33
  end
30
34
 
31
35
  # explicitly indicate the date type of fetched value. run this
@@ -38,7 +42,7 @@ class OCI8
38
42
  # cursor.define(2, Time) # fetch the second column as Time.
39
43
  # cursor.exec()
40
44
  def define(pos, type, length = nil)
41
- bindobj = make_bind_object(:type => type, :length => length)
45
+ bindobj = make_bind_object({:type => type, :length => length}, @fetch_array_size || 1)
42
46
  __define(pos, bindobj)
43
47
  if old = @define_handles[pos - 1]
44
48
  old.send(:free)
@@ -58,15 +62,16 @@ class OCI8
58
62
  # # ...or...
59
63
  # cursor.bind_param(':ename', 'SMITH') # bind by name
60
64
  #
61
- # To bind as number, Fixnum and Float are available, but Bignum is
62
- # not supported. If its initial value is NULL, please set nil to
63
- # +type+ and Fixnum or Float to +val+.
65
+ # To bind as number, set the number intself to +val+. If its initial value
66
+ # is NULL, please set nil to +type+ and Integer, Float or OraNumber to +val+.
64
67
  #
65
68
  # example:
66
- # cursor.bind_param(1, 1234) # bind as Fixnum, Initial value is 1234.
69
+ # cursor.bind_param(1, 1234) # bind as Integer, Initial value is 1234.
67
70
  # cursor.bind_param(1, 1234.0) # bind as Float, Initial value is 1234.0.
68
- # cursor.bind_param(1, nil, Fixnum) # bind as Fixnum, Initial value is NULL.
71
+ # cursor.bind_param(1, nil, Integer) # bind as Integer, Initial value is NULL.
69
72
  # cursor.bind_param(1, nil, Float) # bind as Float, Initial value is NULL.
73
+ # cursor.bind_param(1, OraNumber(1234)) # bind as OraNumber, Initial value is 1234.
74
+ # cursor.bind_param(1, nil, OraNumber) # bind as OraNumber, Initial value is NULL.
70
75
  #
71
76
  # In case of binding a string, set the string itself to
72
77
  # +val+. When the bind variable is used as output, set the
@@ -124,7 +129,10 @@ class OCI8
124
129
  case type
125
130
  when :select_stmt
126
131
  __execute(0)
127
- define_columns()
132
+ define_columns() if @column_metadata.size == 0
133
+ @rowbuf_size = 0
134
+ @rowbuf_index = 0
135
+ @column_metadata.size
128
136
  else
129
137
  __execute(1)
130
138
  row_count
@@ -379,9 +387,10 @@ class OCI8
379
387
  #
380
388
  # FYI: Rails oracle adaptor uses 100 by default.
381
389
  #
382
- # @param [Fixnum] rows The number of rows to be prefetched
390
+ # @param [Integer] rows The number of rows to be prefetched
383
391
  def prefetch_rows=(rows)
384
392
  attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
393
+ @prefetch_rows = rows
385
394
  end
386
395
 
387
396
  if OCI8::oracle_client_version >= ORAVER_12_1
@@ -430,12 +439,12 @@ class OCI8
430
439
  # * OCI8::STMT_ALTER
431
440
  # * OCI8::STMT_BEGIN (PL/SQL block which starts with a BEGIN keyword)
432
441
  # * OCI8::STMT_DECLARE (PL/SQL block which starts with a DECLARE keyword)
433
- # * Other Fixnum value undocumented in Oracle manuals.
442
+ # * Other Integer value undocumented in Oracle manuals.
434
443
  #
435
444
  # <em>Changes between ruby-oci8 1.0 and 2.0.</em>
436
445
  #
437
446
  # [ruby-oci8 2.0] OCI8::STMT_* are Symbols. (:select_stmt, :update_stmt, etc.)
438
- # [ruby-oci8 1.0] OCI8::STMT_* are Fixnums. (1, 2, 3, etc.)
447
+ # [ruby-oci8 1.0] OCI8::STMT_* are Integers. (1, 2, 3, etc.)
439
448
  #
440
449
  def type
441
450
  # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5506
@@ -466,7 +475,7 @@ class OCI8
466
475
 
467
476
  private
468
477
 
469
- def make_bind_object(param)
478
+ def make_bind_object(param, fetch_array_size = nil)
470
479
  case param
471
480
  when Hash
472
481
  key = param[:type]
@@ -508,26 +517,38 @@ class OCI8
508
517
  OCI8::BindType::Mapping[key] = bindclass if bindclass
509
518
  end
510
519
  raise "unsupported datatype: #{key}" if bindclass.nil?
511
- bindclass.create(@con, val, param, max_array_size)
520
+ bindclass.create(@con, val, param, fetch_array_size || max_array_size)
512
521
  end
513
522
 
523
+ @@use_array_fetch = false
524
+
514
525
  def define_columns
515
526
  # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5494
516
527
  num_cols = attr_get_ub4(18) # OCI_ATTR_PARAM_COUNT(18)
517
- 1.upto(num_cols) do |i|
518
- parm = __paramGet(i)
519
- define_one_column(i, parm) unless @define_handles[i - 1]
520
- @column_metadata[i - 1] = parm
528
+ @column_metadata = 1.upto(num_cols).collect do |i|
529
+ __paramGet(i)
530
+ end
531
+ if @define_handles.size == 0
532
+ use_array_fetch = @@use_array_fetch
533
+ @column_metadata.each do |md|
534
+ case md.data_type
535
+ when :clob, :blob, :bfile
536
+ # Rows prefetching doesn't work for CLOB, BLOB and BFILE.
537
+ # Use array fetching to get more than one row in a network round trip.
538
+ use_array_fetch = true
539
+ end
540
+ end
541
+ @fetch_array_size = @prefetch_rows if use_array_fetch
542
+ end
543
+ @column_metadata.each_with_index do |md, i|
544
+ define_one_column(i + 1, md) unless @define_handles[i]
521
545
  end
522
546
  num_cols
523
547
  end
524
548
 
525
549
  def define_one_column(pos, param)
526
- bindobj = make_bind_object(param)
550
+ bindobj = make_bind_object(param, @fetch_array_size || 1)
527
551
  __define(pos, bindobj)
528
- if old = @define_handles[pos - 1]
529
- old.send(:free)
530
- end
531
552
  @define_handles[pos - 1] = bindobj
532
553
  end
533
554
 
@@ -541,22 +562,33 @@ class OCI8
541
562
  end
542
563
  end
543
564
 
565
+ def fetch_row_internal
566
+ if @rowbuf_size && @rowbuf_size == @rowbuf_index
567
+ @rowbuf_size = __fetch(@con, @fetch_array_size || 1)
568
+ @rowbuf_index = 0
569
+ end
570
+ @rowbuf_size
571
+ end
572
+
544
573
  def fetch_one_row_as_array
545
- if __fetch(@con)
546
- @define_handles.collect do |handle|
547
- handle.send(:get_data)
574
+ if fetch_row_internal
575
+ ret = @define_handles.collect do |handle|
576
+ handle.send(:get_data, @rowbuf_index)
548
577
  end
578
+ @rowbuf_index += 1
579
+ ret
549
580
  else
550
581
  nil
551
582
  end
552
583
  end
553
584
 
554
585
  def fetch_one_row_as_hash
555
- if __fetch(@con)
586
+ if fetch_row_internal
556
587
  ret = {}
557
588
  get_col_names.each_with_index do |name, idx|
558
- ret[name] = @define_handles[idx].send(:get_data)
589
+ ret[name] = @define_handles[idx].send(:get_data, @rowbuf_index)
559
590
  end
591
+ @rowbuf_index += 1
560
592
  ret
561
593
  else
562
594
  nil
data/lib/oci8/metadata.rb CHANGED
@@ -1503,7 +1503,11 @@ class OCI8
1503
1503
  attr_get_sb1(OCI_ATTR_SCALE)
1504
1504
  end
1505
1505
 
1506
- # The datatype levels. This attribute always returns zero.
1506
+ # The nest level.
1507
+ #
1508
+ # Oracle manual says that it always returns zero. However it returns
1509
+ # the depth of {OCI8::ArgBase#arguments} calls when #arguments returns
1510
+ # a non-empty array.
1507
1511
  def level
1508
1512
  attr_get_ub2(OCI_ATTR_LEVEL)
1509
1513
  end
@@ -1607,6 +1611,10 @@ class OCI8
1607
1611
 
1608
1612
  # The list of arguments at the next level (when the argument is
1609
1613
  # of a record or table type).
1614
+ #
1615
+ # This method returns an array containing type information when
1616
+ # the type is a user-defined type and the Oracle server version
1617
+ # is 12c or earlier. Otherwise, it returns an empty array.
1610
1618
  def arguments
1611
1619
  @arguments ||= list_arguments.to_a
1612
1620
  end
data/lib/oci8/object.rb CHANGED
@@ -493,6 +493,16 @@ EOS
493
493
  Proc.new do |val| datetime_to_array(val, :date) end, # set_proc
494
494
  Proc.new do |val| array_to_time(val, :local) end, # get_proc
495
495
  ]
496
+ when :timestamp
497
+ [ATTR_TIMESTAMP, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
498
+ Proc.new do |val| datetime_to_array(val, :timestamp) end, # set_proc
499
+ Proc.new do |val| array_to_time(val, :local) end, # get_proc
500
+ ]
501
+ when :timestamp_tz
502
+ [ATTR_TIMESTAMP_TZ, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
503
+ Proc.new do |val| datetime_to_array(val, :timestamp_tz) end, # set_proc
504
+ Proc.new do |val| array_to_time(val, nil) end, # get_proc
505
+ ]
496
506
  when :binary_double
497
507
  [ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
498
508
  when :binary_float