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.
- 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
|