ruby-oci8 2.2.4.1 → 2.2.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/ChangeLog +311 -1
  3. data/NEWS +278 -46
  4. data/README.md +5 -2
  5. data/dist-files +8 -4
  6. data/docs/bind-array-to-in_cond.md +1 -1
  7. data/docs/install-instant-client.md +2 -1
  8. data/docs/install-on-osx.md +29 -116
  9. data/docs/ldap-auth-and-function-interposition.md +123 -0
  10. data/docs/number-type-mapping.md +79 -0
  11. data/ext/oci8/apiwrap.c.tmpl +2 -5
  12. data/ext/oci8/apiwrap.rb +6 -1
  13. data/ext/oci8/apiwrap.yml +34 -22
  14. data/ext/oci8/attr.c +4 -2
  15. data/ext/oci8/bind.c +366 -6
  16. data/ext/oci8/connection_pool.c +3 -3
  17. data/ext/oci8/error.c +18 -33
  18. data/ext/oci8/extconf.rb +7 -4
  19. data/ext/oci8/hook_funcs.c +128 -51
  20. data/ext/oci8/lob.c +31 -75
  21. data/ext/oci8/metadata.c +2 -2
  22. data/ext/oci8/object.c +72 -27
  23. data/ext/oci8/oci8.c +27 -119
  24. data/ext/oci8/oci8.h +21 -3
  25. data/ext/oci8/oci8lib.c +50 -37
  26. data/ext/oci8/ocihandle.c +2 -2
  27. data/ext/oci8/ocinumber.c +22 -16
  28. data/ext/oci8/oraconf.rb +130 -257
  29. data/ext/oci8/oradate.c +1 -1
  30. data/ext/oci8/plthook_elf.c +384 -300
  31. data/ext/oci8/plthook_osx.c +10 -10
  32. data/ext/oci8/stmt.c +51 -16
  33. data/ext/oci8/win32.c +4 -22
  34. data/lib/oci8/bindtype.rb +1 -15
  35. data/lib/oci8/check_load_error.rb +57 -10
  36. data/lib/oci8/cursor.rb +48 -17
  37. data/lib/oci8/metadata.rb +9 -1
  38. data/lib/oci8/object.rb +10 -0
  39. data/lib/oci8/oci8.rb +26 -25
  40. data/lib/oci8/oracle_version.rb +11 -1
  41. data/lib/oci8/version.rb +1 -1
  42. data/lib/oci8.rb +11 -4
  43. data/lib/ruby-oci8.rb +0 -3
  44. data/ruby-oci8.gemspec +2 -3
  45. data/setup.rb +11 -2
  46. data/test/README.md +37 -0
  47. data/test/config.rb +1 -1
  48. data/test/setup_test_object.sql +21 -13
  49. data/test/setup_test_package.sql +59 -0
  50. data/test/test_all.rb +1 -0
  51. data/test/test_bind_boolean.rb +99 -0
  52. data/test/test_break.rb +11 -9
  53. data/test/test_clob.rb +4 -16
  54. data/test/test_datetime.rb +8 -3
  55. data/test/test_object.rb +33 -9
  56. data/test/test_oci8.rb +169 -45
  57. data/test/test_oranumber.rb +12 -6
  58. data/test/test_package_type.rb +15 -3
  59. data/test/test_properties.rb +17 -0
  60. metadata +40 -57
  61. data/docs/osx-install-dev-tools.png +0 -0
  62. 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
  {
@@ -148,7 +148,7 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *hndl)
148
148
  RTLD_LAZY | RTLD_NOLOAD,
149
149
  RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST,
150
150
  };
151
- int flag_idx;
151
+ size_t flag_idx;
152
152
  #define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
153
153
 
154
154
  if (hndl == NULL) {
@@ -196,8 +196,8 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
196
196
  struct segment_command_ *segments[NUM_SEGMENTS];
197
197
  int segment_idx = 0;
198
198
  unsigned int nbind;
199
- int addrdiff = 0;
200
- int i;
199
+ ptrdiff_t addrdiff = 0;
200
+ uint32_t i;
201
201
 
202
202
  memset(segments, 0, sizeof(segments));
203
203
  #ifdef __LP64__
@@ -320,14 +320,14 @@ static int plthook_open_real(plthook_t **plthook_out, const struct mach_header *
320
320
  return 0;
321
321
  }
322
322
 
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, 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)
324
324
  {
325
325
  const uint8_t *ptr = base + lazy_bind_off + addrdiff;
326
326
  const uint8_t *end = ptr + lazy_bind_size;
327
327
  const char *sym_name;
328
328
  int seg_index = 0;
329
329
  uint64_t seg_offset = 0;
330
- int count, skip;
330
+ uint64_t count, skip;
331
331
  unsigned int idx;
332
332
  DEBUG_BIND("get_bind_addr(%p, 0x%x, 0x%x", base, lazy_bind_off, lazy_bind_size);
333
333
  for (idx = 0; segments[idx] != NULL; idx++) {
@@ -341,7 +341,7 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
341
341
  uint8_t imm = *ptr & BIND_IMMEDIATE_MASK;
342
342
  uint64_t ulebval;
343
343
  int64_t slebval;
344
- int i;
344
+ uint64_t i;
345
345
 
346
346
  DEBUG_BIND("0x%02x: ", *ptr);
347
347
  ptr++;
@@ -410,10 +410,10 @@ static unsigned int get_bind_addr(plthook_t *plthook, const uint8_t *base, uint3
410
410
  return idx;
411
411
  }
412
412
 
413
- 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)
414
414
  {
415
415
  if (plthook != NULL) {
416
- uint32_t vmaddr = segments[seg_index]->vmaddr;
416
+ uint64_t vmaddr = segments[seg_index]->vmaddr;
417
417
  plthook->entries[*idx].name = sym_name;
418
418
  plthook->entries[*idx].addr = (void**)(base + vmaddr + seg_offset);
419
419
  }
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
  /*
@@ -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)
@@ -125,7 +129,10 @@ class OCI8
125
129
  case type
126
130
  when :select_stmt
127
131
  __execute(0)
128
- define_columns()
132
+ define_columns() if @column_metadata.size == 0
133
+ @rowbuf_size = 0
134
+ @rowbuf_index = 0
135
+ @column_metadata.size
129
136
  else
130
137
  __execute(1)
131
138
  row_count
@@ -383,6 +390,7 @@ class OCI8
383
390
  # @param [Integer] rows The number of rows to be prefetched
384
391
  def prefetch_rows=(rows)
385
392
  attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
393
+ @prefetch_rows = rows
386
394
  end
387
395
 
388
396
  if OCI8::oracle_client_version >= ORAVER_12_1
@@ -467,7 +475,7 @@ class OCI8
467
475
 
468
476
  private
469
477
 
470
- def make_bind_object(param)
478
+ def make_bind_object(param, fetch_array_size = nil)
471
479
  case param
472
480
  when Hash
473
481
  key = param[:type]
@@ -509,26 +517,38 @@ class OCI8
509
517
  OCI8::BindType::Mapping[key] = bindclass if bindclass
510
518
  end
511
519
  raise "unsupported datatype: #{key}" if bindclass.nil?
512
- bindclass.create(@con, val, param, max_array_size)
520
+ bindclass.create(@con, val, param, fetch_array_size || max_array_size)
513
521
  end
514
522
 
523
+ @@use_array_fetch = false
524
+
515
525
  def define_columns
516
526
  # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5494
517
527
  num_cols = attr_get_ub4(18) # OCI_ATTR_PARAM_COUNT(18)
518
- 1.upto(num_cols) do |i|
519
- parm = __paramGet(i)
520
- define_one_column(i, parm) unless @define_handles[i - 1]
521
- @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]
522
545
  end
523
546
  num_cols
524
547
  end
525
548
 
526
549
  def define_one_column(pos, param)
527
- bindobj = make_bind_object(param)
550
+ bindobj = make_bind_object(param, @fetch_array_size || 1)
528
551
  __define(pos, bindobj)
529
- if old = @define_handles[pos - 1]
530
- old.send(:free)
531
- end
532
552
  @define_handles[pos - 1] = bindobj
533
553
  end
534
554
 
@@ -542,22 +562,33 @@ class OCI8
542
562
  end
543
563
  end
544
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
+
545
573
  def fetch_one_row_as_array
546
- if __fetch(@con)
547
- @define_handles.collect do |handle|
548
- handle.send(:get_data)
574
+ if fetch_row_internal
575
+ ret = @define_handles.collect do |handle|
576
+ handle.send(:get_data, @rowbuf_index)
549
577
  end
578
+ @rowbuf_index += 1
579
+ ret
550
580
  else
551
581
  nil
552
582
  end
553
583
  end
554
584
 
555
585
  def fetch_one_row_as_hash
556
- if __fetch(@con)
586
+ if fetch_row_internal
557
587
  ret = {}
558
588
  get_col_names.each_with_index do |name, idx|
559
- ret[name] = @define_handles[idx].send(:get_data)
589
+ ret[name] = @define_handles[idx].send(:get_data, @rowbuf_index)
560
590
  end
591
+ @rowbuf_index += 1
561
592
  ret
562
593
  else
563
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
data/lib/oci8/oci8.rb CHANGED
@@ -113,7 +113,7 @@ class OCI8
113
113
  if dbname.is_a? OCI8::ConnectionPool
114
114
  @pool = dbname # to prevent GC from freeing the connection pool.
115
115
  dbname = dbname.send(:pool_name)
116
- attach_mode |= 0x0200 # OCI_CPOOL and OCI_LOGON2_CPOOL
116
+ attach_mode |= 0x0200 # OCI_CPOOL
117
117
  else
118
118
  tcp_connect_timeout = OCI8::properties[:tcp_connect_timeout]
119
119
  connect_timeout = OCI8::properties[:connect_timeout]
@@ -124,32 +124,27 @@ class OCI8
124
124
  end
125
125
  if stmt_cache_size
126
126
  # enable statement caching
127
- attach_mode |= 0x0004 # OCI_STMT_CACHE and OCI_LOGON2_STMTCACHE
127
+ attach_mode |= 0x0004 # OCI_STMT_CACHE
128
128
  end
129
129
 
130
- if true
131
- # logon by the OCI function OCISessionBegin().
132
- allocate_handles()
133
- @session_handle.send(:attr_set_string, OCI_ATTR_USERNAME, username) if username
134
- @session_handle.send(:attr_set_string, OCI_ATTR_PASSWORD, password) if password
135
- if @@oracle_client_version >= ORAVER_11_1
136
- # Sets the driver name displayed in V$SESSION_CONNECT_INFO.CLIENT_DRIVER
137
- # if both the client and the server are Oracle 11g or upper.
138
- # Only the first 8 chracters "ruby-oci" are displayed when the Oracle
139
- # server version is lower than 12.0.1.2.
140
- # 424: OCI_ATTR_DRIVER_NAME
141
- @session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
142
- end
143
- server_attach(dbname, attach_mode)
144
- if OCI8.oracle_client_version >= OCI8::ORAVER_11_1
145
- self.send_timeout = OCI8::properties[:send_timeout] if OCI8::properties[:send_timeout]
146
- self.recv_timeout = OCI8::properties[:recv_timeout] if OCI8::properties[:recv_timeout]
147
- end
148
- session_begin(cred ? cred : OCI_CRED_RDBMS, auth_mode)
149
- else
150
- # logon by the OCI function OCILogon2().
151
- logon2(username, password, dbname, attach_mode)
130
+ # logon by the OCI function OCISessionBegin().
131
+ allocate_handles()
132
+ @session_handle.send(:attr_set_string, OCI_ATTR_USERNAME, username) if username
133
+ @session_handle.send(:attr_set_string, OCI_ATTR_PASSWORD, password) if password
134
+ if @@oracle_client_version >= ORAVER_11_1
135
+ # Sets the driver name displayed in V$SESSION_CONNECT_INFO.CLIENT_DRIVER
136
+ # if both the client and the server are Oracle 11g or upper.
137
+ # Only the first 8 chracters "ruby-oci" are displayed when the Oracle
138
+ # server version is lower than 12.0.1.2.
139
+ # 424: OCI_ATTR_DRIVER_NAME
140
+ @session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
152
141
  end
142
+ server_attach(dbname, attach_mode)
143
+ if OCI8.oracle_client_version >= OCI8::ORAVER_11_1
144
+ self.send_timeout = OCI8::properties[:send_timeout] if OCI8::properties[:send_timeout]
145
+ self.recv_timeout = OCI8::properties[:recv_timeout] if OCI8::properties[:recv_timeout]
146
+ end
147
+ session_begin(cred ? cred : OCI_CRED_RDBMS, auth_mode)
153
148
 
154
149
  if stmt_cache_size
155
150
  # set statement cache size
@@ -174,7 +169,6 @@ class OCI8
174
169
  # @private
175
170
  def parse_internal(sql)
176
171
  cursor = OCI8::Cursor.new(self, sql)
177
- cursor.prefetch_rows = @prefetch_rows if @prefetch_rows
178
172
  cursor
179
173
  end
180
174
 
@@ -310,6 +304,7 @@ class OCI8
310
304
  # @return [Array] an array of first row.
311
305
  def select_one(sql, *bindvars)
312
306
  cursor = self.parse(sql)
307
+ cursor.prefetch_rows = 1
313
308
  begin
314
309
  cursor.exec(*bindvars)
315
310
  row = cursor.fetch
@@ -345,6 +340,12 @@ class OCI8
345
340
 
346
341
  # Returns the Oracle server version.
347
342
  #
343
+ # When the Oracle client version is 12c or earlier and
344
+ # the Oracle server version is 18c or later, this method
345
+ # doesn't return *full* version number such as '18.3.0.0.0'.
346
+ # It returns version number whose number components after
347
+ # the first dot are zeros such as '18.0.0.0.0'.
348
+ #
348
349
  # @see OCI8.oracle_client_version
349
350
  # @return [OCI8::OracleVersion]
350
351
  def oracle_server_version