ruby-oci8 2.2.4.1 → 2.2.11
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.
- checksums.yaml +7 -0
- data/ChangeLog +311 -1
- data/NEWS +278 -46
- data/README.md +5 -2
- data/dist-files +8 -4
- data/docs/bind-array-to-in_cond.md +1 -1
- data/docs/install-instant-client.md +2 -1
- data/docs/install-on-osx.md +29 -116
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/ext/oci8/apiwrap.c.tmpl +2 -5
- data/ext/oci8/apiwrap.rb +6 -1
- data/ext/oci8/apiwrap.yml +34 -22
- data/ext/oci8/attr.c +4 -2
- data/ext/oci8/bind.c +366 -6
- data/ext/oci8/connection_pool.c +3 -3
- data/ext/oci8/error.c +18 -33
- data/ext/oci8/extconf.rb +7 -4
- data/ext/oci8/hook_funcs.c +128 -51
- data/ext/oci8/lob.c +31 -75
- data/ext/oci8/metadata.c +2 -2
- data/ext/oci8/object.c +72 -27
- data/ext/oci8/oci8.c +27 -119
- data/ext/oci8/oci8.h +21 -3
- data/ext/oci8/oci8lib.c +50 -37
- data/ext/oci8/ocihandle.c +2 -2
- data/ext/oci8/ocinumber.c +22 -16
- data/ext/oci8/oraconf.rb +130 -257
- data/ext/oci8/oradate.c +1 -1
- data/ext/oci8/plthook_elf.c +384 -300
- data/ext/oci8/plthook_osx.c +10 -10
- data/ext/oci8/stmt.c +51 -16
- data/ext/oci8/win32.c +4 -22
- data/lib/oci8/bindtype.rb +1 -15
- data/lib/oci8/check_load_error.rb +57 -10
- data/lib/oci8/cursor.rb +48 -17
- data/lib/oci8/metadata.rb +9 -1
- data/lib/oci8/object.rb +10 -0
- data/lib/oci8/oci8.rb +26 -25
- data/lib/oci8/oracle_version.rb +11 -1
- data/lib/oci8/version.rb +1 -1
- data/lib/oci8.rb +11 -4
- data/lib/ruby-oci8.rb +0 -3
- data/ruby-oci8.gemspec +2 -3
- data/setup.rb +11 -2
- data/test/README.md +37 -0
- data/test/config.rb +1 -1
- data/test/setup_test_object.sql +21 -13
- data/test/setup_test_package.sql +59 -0
- data/test/test_all.rb +1 -0
- data/test/test_bind_boolean.rb +99 -0
- data/test/test_break.rb +11 -9
- data/test/test_clob.rb +4 -16
- data/test/test_datetime.rb +8 -3
- data/test/test_object.rb +33 -9
- data/test/test_oci8.rb +169 -45
- data/test/test_oranumber.rb +12 -6
- data/test/test_package_type.rb +15 -3
- data/test/test_properties.rb +17 -0
- metadata +40 -57
- data/docs/osx-install-dev-tools.png +0 -0
- data/test/README +0 -42
data/ext/oci8/plthook_osx.c
CHANGED
@@ -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,
|
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,
|
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
|
-
|
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
|
-
|
200
|
-
|
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,
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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),
|
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
|
-
|
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
|
-
|
181
|
+
ub4 len = RSTRING_LENINT(symval);
|
167
182
|
#else
|
168
183
|
const char *symname = rb_id2name(SYM2ID(vplaceholder));
|
169
|
-
|
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 =
|
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
|
-
|
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,
|
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,
|
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,
|
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
|
-
|
312
|
+
stmt->end_of_fetch = 1;
|
313
|
+
} else {
|
314
|
+
chker3(rv, &svcctx->base, stmt->base.hp.stmt);
|
281
315
|
}
|
282
|
-
|
283
|
-
|
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,
|
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(
|
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(
|
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.
|
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|
|
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
|
-
|
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: / # "
|
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
|
29
|
-
paths << buf[0, GetSystemDirectoryA
|
30
|
-
paths << buf[0, GetWindowsDirectoryA
|
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
|
-
|
520
|
-
|
521
|
-
|
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
|
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
|
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
|
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
|
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
|
127
|
+
attach_mode |= 0x0004 # OCI_STMT_CACHE
|
128
128
|
end
|
129
129
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|