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.
- checksums.yaml +7 -0
- data/ChangeLog +427 -0
- data/NEWS +335 -42
- data/README.md +20 -9
- data/dist-files +9 -3
- data/docs/bind-array-to-in_cond.md +2 -2
- data/docs/conflicts-local-connections-and-processes.md +7 -4
- data/docs/hanging-after-inactivity.md +63 -0
- data/docs/install-binary-package.md +15 -11
- data/docs/install-full-client.md +18 -21
- data/docs/install-instant-client.md +45 -27
- data/docs/install-on-osx.md +31 -120
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/docs/platform-specific-issues.md +17 -50
- data/docs/report-installation-issue.md +3 -0
- data/docs/timeout-parameters.md +3 -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/encoding.c +5 -5
- data/ext/oci8/env.c +8 -2
- data/ext/oci8/error.c +24 -16
- data/ext/oci8/extconf.rb +8 -4
- data/ext/oci8/hook_funcs.c +274 -61
- 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 +45 -132
- data/ext/oci8/oci8.h +32 -88
- data/ext/oci8/oci8lib.c +178 -38
- data/ext/oci8/ocihandle.c +37 -37
- data/ext/oci8/ocinumber.c +23 -18
- data/ext/oci8/oraconf.rb +158 -339
- data/ext/oci8/oradate.c +19 -19
- data/ext/oci8/plthook.h +10 -0
- data/ext/oci8/plthook_elf.c +433 -268
- data/ext/oci8/plthook_osx.c +40 -9
- data/ext/oci8/plthook_win32.c +9 -0
- data/ext/oci8/stmt.c +52 -17
- 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 +57 -25
- data/lib/oci8/metadata.rb +9 -1
- data/lib/oci8/object.rb +10 -0
- data/lib/oci8/oci8.rb +33 -28
- data/lib/oci8/oracle_version.rb +11 -1
- data/lib/oci8/properties.rb +22 -0
- data/lib/oci8/version.rb +1 -1
- data/lib/oci8.rb +48 -4
- data/lib/ruby-oci8.rb +0 -3
- data/pre-distclean.rb +1 -3
- data/ruby-oci8.gemspec +3 -8
- 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 +2 -0
- data/test/test_bind_boolean.rb +99 -0
- data/test/test_bind_integer.rb +47 -0
- data/test/test_break.rb +11 -9
- data/test/test_clob.rb +4 -16
- data/test/test_connstr.rb +29 -13
- data/test/test_datetime.rb +8 -3
- data/test/test_object.rb +27 -9
- data/test/test_oci8.rb +170 -46
- data/test/test_oranumber.rb +12 -6
- data/test/test_package_type.rb +15 -3
- data/test/test_properties.rb +17 -0
- metadata +40 -54
- 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
|
{
|
@@ -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
|
-
|
169
|
-
|
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,
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
}
|
data/ext/oci8/plthook_win32.c
CHANGED
@@ -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
|
-
|
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
|
/*
|
@@ -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 [
|
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,
|
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)
|
@@ -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,
|
62
|
-
#
|
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
|
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,
|
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 [
|
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
|
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
|
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
|
-
|
519
|
-
|
520
|
-
|
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
|
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
|
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
|
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
|