ruby-oci8 2.1.2 → 2.1.3

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.
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * oci8.h - part of ruby-oci8
4
4
  *
5
- * Copyright (C) 2002-2011 KUBO Takehiro <kubo@jiubao.org>
5
+ * Copyright (C) 2002-2012 KUBO Takehiro <kubo@jiubao.org>
6
6
  */
7
7
  #ifndef _RUBY_OCI_H_
8
8
  #define _RUBY_OCI_H_ 1
@@ -126,7 +126,20 @@ typedef struct OCICPool OCICPool;
126
126
  #define STRINGIZE(name) #name
127
127
  #endif
128
128
  #ifndef RB_GC_GUARD
129
- #define RB_GC_GUARD(v) (*(volatile VALUE *)&(v))
129
+ #ifdef __GNUC__
130
+ #define RB_GC_GUARD_PTR(ptr) \
131
+ __extension__ ({volatile VALUE *rb_gc_guarded_ptr = (ptr); rb_gc_guarded_ptr;})
132
+ #else
133
+ #ifdef _MSC_VER
134
+ #pragma optimize("", off)
135
+ #endif
136
+ static inline volatile VALUE *rb_gc_guarded_ptr(volatile VALUE *ptr) {return ptr;}
137
+ #ifdef _MSC_VER
138
+ #pragma optimize("", on)
139
+ #endif
140
+ #define RB_GC_GUARD_PTR(ptr) rb_gc_guarded_ptr(ptr)
141
+ #endif
142
+ #define RB_GC_GUARD(v) (*RB_GC_GUARD_PTR(&(v)))
130
143
  #endif
131
144
 
132
145
  /* new functions in ruby 1.9.
@@ -135,9 +148,6 @@ typedef struct OCICPool OCICPool;
135
148
  #if !defined(HAVE_RB_ERRINFO) && defined(HAVE_RUBY_ERRINFO)
136
149
  #define rb_errinfo() ruby_errinfo
137
150
  #endif
138
- #if !defined HAVE_TYPE_RB_BLOCKING_FUNCTION_T_ && !defined HAVE_TYPE_RB_BLOCKING_FUNCTION_TP
139
- typedef VALUE rb_blocking_function_t(void *);
140
- #endif
141
151
 
142
152
  #ifndef HAVE_TYPE_RB_ENCODING
143
153
  #define rb_enc_associate(str, enc) do {} while(0)
@@ -160,7 +170,11 @@ typedef VALUE rb_blocking_function_t(void *);
160
170
  #endif
161
171
  #endif
162
172
 
163
- #if defined(HAVE_NATIVETHREAD) || defined(HAVE_RB_THREAD_BLOCKING_REGION)
173
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) || defined(HAVE_RB_THREAD_BLOCKING_REGION)
174
+ #define NATIVE_THREAD_WITH_GVL 1
175
+ #endif
176
+
177
+ #if defined(HAVE_NATIVETHREAD) || NATIVE_THREAD_WITH_GVL
164
178
  /*
165
179
  * oci8_errhp is a thread local object in ruby 1.9, rubinius
166
180
  * and ruby 1.8 configured with --enable-pthread.
@@ -213,7 +227,7 @@ typedef VALUE rb_blocking_function_t(void *);
213
227
  #define oci8_tls_get(key) pthread_getspecific(key)
214
228
  #define oci8_tls_set(key, val) pthread_setspecific((key), (val))
215
229
  #endif
216
- #endif /* HAVE_RB_THREAD_BLOCKING_REGION */
230
+ #endif /* USE_THREAD_LOCAL_ERRHP */
217
231
 
218
232
  /* utility macros
219
233
  */
@@ -333,6 +347,11 @@ struct oci8_bind {
333
347
 
334
348
  typedef struct oci8_logoff_strategy oci8_logoff_strategy_t;
335
349
 
350
+ typedef struct oci8_temp_lob {
351
+ struct oci8_temp_lob *next;
352
+ OCILobLocator *lob;
353
+ } oci8_temp_lob_t;
354
+
336
355
  typedef struct oci8_svcctx {
337
356
  oci8_base_t base;
338
357
  volatile VALUE executing_thread;
@@ -342,15 +361,16 @@ typedef struct oci8_svcctx {
342
361
  rb_pid_t pid;
343
362
  unsigned char state;
344
363
  char is_autocommit;
345
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
364
+ #ifdef NATIVE_THREAD_WITH_GVL
346
365
  char non_blocking;
347
366
  #endif
348
367
  VALUE long_read_len;
368
+ oci8_temp_lob_t *temp_lobs;
349
369
  } oci8_svcctx_t;
350
370
 
351
371
  struct oci8_logoff_strategy {
352
372
  void *(*prepare)(oci8_svcctx_t *svcctx);
353
- rb_blocking_function_t *execute;
373
+ void *(*execute)(void *);
354
374
  };
355
375
 
356
376
  typedef struct {
@@ -428,7 +448,6 @@ extern ID oci8_id_at_last_error;
428
448
  extern ID oci8_id_new;
429
449
  extern ID oci8_id_get;
430
450
  extern ID oci8_id_set;
431
- extern ID oci8_id_keys;
432
451
  extern ID oci8_id_oci8_vtable;
433
452
  #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
434
453
  extern ID oci8_id_add_op; /* ID of the addition operator '+' */
@@ -449,7 +468,7 @@ VALUE oci8_define_class_under(VALUE outer, const char *name, oci8_base_vtable_t
449
468
  VALUE oci8_define_bind_class(const char *name, const oci8_bind_vtable_t *vptr);
450
469
  void oci8_link_to_parent(oci8_base_t *base, oci8_base_t *parent);
451
470
  void oci8_unlink_from_parent(oci8_base_t *base);
452
- sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, void *data);
471
+ sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data);
453
472
  sword oci8_exec_sql(oci8_svcctx_t *svcctx, const char *sql_text, ub4 num_define_vars, oci8_exec_sql_var_t *define_vars, ub4 num_bind_vars, oci8_exec_sql_var_t *bind_vars, int raise_on_error);
454
473
  #if defined RUNTIME_API_CHECK
455
474
  void *oci8_find_symbol(const char *symbol_name);
@@ -471,7 +490,7 @@ void oci8_check_error_(sword status, oci8_base_t *base, OCIStmt *stmthp, const c
471
490
  void Init_oci8_handle(void);
472
491
 
473
492
  /* oci8.c */
474
- VALUE Init_oci8(void);
493
+ void Init_oci8(VALUE *out);
475
494
  void oci8_do_parse_connect_string(VALUE conn_str, VALUE *user, VALUE *pass, VALUE *dbname, VALUE *mode);
476
495
  oci8_svcctx_t *oci8_get_svcctx(VALUE obj);
477
496
  OCISvcCtx *oci8_get_oci_svcctx(VALUE obj);
@@ -496,8 +515,6 @@ void oci8_bind_free(oci8_base_t *base);
496
515
  void oci8_bind_hp_obj_mark(oci8_base_t *base);
497
516
  void Init_oci8_bind(VALUE cOCI8BindTypeBase);
498
517
  oci8_bind_t *oci8_get_bind(VALUE obj);
499
- void oci8_bind_set_data(VALUE self, VALUE val);
500
- VALUE oci8_bind_get_data(VALUE self);
501
518
 
502
519
  /* metadata.c */
503
520
  extern VALUE cOCI8MetadataBase;
@@ -551,11 +568,7 @@ VALUE oci8_make_interval_ds(OCIInterval *s);
551
568
  void Init_oci_object(VALUE mOCI);
552
569
 
553
570
  /* attr.c */
554
- VALUE oci8_get_sb1_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
555
571
  VALUE oci8_get_ub2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
556
- VALUE oci8_get_sb2_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
557
- VALUE oci8_get_ub4_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
558
- VALUE oci8_get_string_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
559
572
  VALUE oci8_get_rowid_attr(oci8_base_t *base, ub4 attrtype, OCIStmt *stmtp);
560
573
 
561
574
  /* encoding.c */
@@ -4,12 +4,14 @@
4
4
  */
5
5
 
6
6
  #include "oci8.h"
7
+ #ifdef HAVE_RUBY_THREAD_H
8
+ #include <ruby/thread.h>
9
+ #endif
7
10
 
8
11
  ID oci8_id_at_last_error;
9
12
  ID oci8_id_new;
10
13
  ID oci8_id_get;
11
14
  ID oci8_id_set;
12
- ID oci8_id_keys;
13
15
  ID oci8_id_oci8_vtable;
14
16
  #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
15
17
  ID oci8_id_add_op;
@@ -80,7 +82,6 @@ Init_oci8lib()
80
82
  oci8_id_new = rb_intern("new");
81
83
  oci8_id_get = rb_intern("get");
82
84
  oci8_id_set = rb_intern("set");
83
- oci8_id_keys = rb_intern("keys");
84
85
  oci8_id_oci8_vtable = rb_intern("__oci8_vtable__");
85
86
  #ifdef CHAR_IS_NOT_A_SHORTCUT_TO_ID
86
87
  oci8_id_add_op = rb_intern("+");
@@ -100,7 +101,7 @@ Init_oci8lib()
100
101
  Init_oci8_handle();
101
102
 
102
103
  /* OCI8 class */
103
- cOCI8 = Init_oci8();
104
+ Init_oci8(&cOCI8);
104
105
 
105
106
  /* OCI8::ConnectionPool class */
106
107
  Init_oci8_connection_pool(cOCI8);
@@ -201,74 +202,106 @@ void oci8_unlink_from_parent(oci8_base_t *base)
201
202
  base->parent = NULL;
202
203
  }
203
204
 
204
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
205
+ #ifdef NATIVE_THREAD_WITH_GVL
205
206
 
206
- #if 0
207
- typedef struct {
208
- dvoid *hndlp;
207
+ static void oci8_unblock_func(void *user_data)
208
+ {
209
+ oci8_svcctx_t *svcctx = (oci8_svcctx_t *)user_data;
210
+ OCIBreak(svcctx->base.hp.ptr, oci8_errhp);
211
+ }
212
+
213
+ typedef struct free_temp_lob_arg_t {
214
+ oci8_svcctx_t *svcctx;
215
+ OCISvcCtx *svchp;
209
216
  OCIError *errhp;
210
- } ocibreak_arg_t;
217
+ OCILobLocator *lob;
218
+ } free_temp_lob_arg_t;
211
219
 
212
- static VALUE call_OCIBreak(void *user_data)
220
+ static void *free_temp_lob(void *user_data)
213
221
  {
214
- ocibreak_arg_t *arg = (ocibreak_arg_t *)user_data;
215
- OCIBreak(arg->hndlp, arg->errhp);
216
- return Qnil;
222
+ free_temp_lob_arg_t *data = (free_temp_lob_arg_t *)user_data;
223
+ sword rv = OCILobFreeTemporary(data->svchp, data->errhp, data->lob);
224
+
225
+ data->svcctx->executing_thread = Qnil;
226
+ return (void*)(VALUE)rv;
217
227
  }
218
228
 
219
- static void oci8_unblock_func(void *user_data)
229
+ /* ruby 1.9 */
230
+ sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data)
220
231
  {
221
- oci8_svcctx_t *svcctx = (oci8_svcctx_t *)user_data;
222
- if (svcctx->base.hp.ptr != NULL) {
223
- ocibreak_arg_t arg;
224
- arg.hndlp = svcctx->base.hp.ptr;
225
- arg.errhp = oci8_errhp;
226
- rb_thread_blocking_region(call_OCIBreak, &arg, NULL, NULL);
232
+ oci8_temp_lob_t *lob;
233
+ OCIError *errhp = oci8_errhp;
234
+
235
+ if (!NIL_P(svcctx->executing_thread)) {
236
+ rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
227
237
  }
228
- }
238
+
239
+ lob = svcctx->temp_lobs;
240
+ while (lob != NULL) {
241
+ oci8_temp_lob_t *lob_next = lob->next;
242
+
243
+ if (svcctx->non_blocking) {
244
+ free_temp_lob_arg_t arg;
245
+ sword rv;
246
+
247
+ arg.svcctx = svcctx;
248
+ arg.svchp = svcctx->base.hp.svc;
249
+ arg.errhp = errhp;
250
+ arg.lob = lob->lob;
251
+
252
+ svcctx->executing_thread = rb_thread_current();
253
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
254
+ rv = (sword)(VALUE)rb_thread_call_without_gvl(free_temp_lob, &arg, oci8_unblock_func, svcctx);
229
255
  #else
230
- static void oci8_unblock_func(void *user_data)
231
- {
232
- oci8_svcctx_t *svcctx = (oci8_svcctx_t *)user_data;
233
- OCIBreak(svcctx->base.hp.ptr, oci8_errhp);
234
- }
256
+ rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))free_temp_lob, &arg, oci8_unblock_func, svcctx);
235
257
  #endif
258
+ if (rv == OCI_ERROR) {
259
+ if (oci8_get_error_code(errhp) == 1013) {
260
+ rb_raise(eOCIBreak, "Canceled by user request.");
261
+ }
262
+ }
263
+ } else {
264
+ OCILobFreeTemporary(svcctx->base.hp.svc, errhp, lob->lob);
265
+ }
266
+ OCIDescriptorFree(lob->lob, OCI_DTYPE_LOB);
267
+
268
+ xfree(lob);
269
+ svcctx->temp_lobs = lob = lob_next;
270
+ }
236
271
 
237
- /* ruby 1.9 */
238
- sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, void *data)
239
- {
240
272
  if (svcctx->non_blocking) {
241
273
  sword rv;
242
274
 
243
- if (!NIL_P(svcctx->executing_thread)) {
244
- rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
245
- }
246
275
  svcctx->executing_thread = rb_thread_current();
247
276
  /* Note: executing_thread is cleard at the end of the blocking function. */
248
- rv = (sword)rb_thread_blocking_region(func, data, oci8_unblock_func, svcctx);
277
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
278
+ rv = (sword)(VALUE)rb_thread_call_without_gvl(func, data, oci8_unblock_func, svcctx);
279
+ #else
280
+ rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))func, data, oci8_unblock_func, svcctx);
281
+ #endif
249
282
  if (rv == OCI_ERROR) {
250
- if (oci8_get_error_code(oci8_errhp) == 1013) {
283
+ if (oci8_get_error_code(errhp) == 1013) {
251
284
  rb_raise(eOCIBreak, "Canceled by user request.");
252
285
  }
253
286
  }
254
287
  return rv;
255
288
  } else {
256
- return (sword)func(data);
289
+ return (sword)(VALUE)func(data);
257
290
  }
258
291
  }
259
- #else /* HAVE_RB_THREAD_BLOCKING_REGION */
292
+ #else /* NATIVE_THREAD_WITH_GVL */
260
293
 
261
294
  /* ruby 1.8 */
262
295
  typedef struct {
263
296
  oci8_svcctx_t *svcctx;
264
- rb_blocking_function_t *func;
297
+ void *(*func)(void *);
265
298
  void *data;
266
299
  } blocking_region_arg_t;
267
300
 
268
301
  static VALUE blocking_function_execute(blocking_region_arg_t *arg)
269
302
  {
270
303
  oci8_svcctx_t *svcctx = arg->svcctx;
271
- rb_blocking_function_t *func = arg->func;
304
+ void *(*func)(void *) = arg->func;
272
305
  void *data = arg->data;
273
306
  struct timeval tv;
274
307
  sword rv;
@@ -276,7 +309,7 @@ static VALUE blocking_function_execute(blocking_region_arg_t *arg)
276
309
  tv.tv_sec = 0;
277
310
  tv.tv_usec = 10000;
278
311
  svcctx->executing_thread = rb_thread_current();
279
- while ((rv = func(data)) == OCI_STILL_EXECUTING) {
312
+ while ((rv = (sword)(VALUE)func(data)) == OCI_STILL_EXECUTING) {
280
313
  rb_thread_wait_for(tv);
281
314
  if (tv.tv_usec < 500000)
282
315
  tv.tv_usec <<= 1;
@@ -303,7 +336,7 @@ static VALUE blocking_function_ensure(oci8_svcctx_t *svcctx)
303
336
  return Qnil;
304
337
  }
305
338
 
306
- sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, void *data)
339
+ sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data)
307
340
  {
308
341
  blocking_region_arg_t arg;
309
342
 
@@ -315,7 +348,7 @@ sword oci8_blocking_region(oci8_svcctx_t *svcctx, rb_blocking_function_t func, v
315
348
  }
316
349
  return (sword)rb_ensure(blocking_function_execute, (VALUE)&arg, blocking_function_ensure, (VALUE)svcctx);
317
350
  }
318
- #endif /* HAVE_RB_THREAD_BLOCKING_REGION */
351
+ #endif /* NATIVE_THREAD_WITH_GVL */
319
352
 
320
353
  typedef struct {
321
354
  oci8_svcctx_t *svcctx;
@@ -419,8 +419,9 @@ static VALUE attr_get_binary(VALUE self, VALUE attr_type)
419
419
  static VALUE attr_get_integer(VALUE self, VALUE attr_type)
420
420
  {
421
421
  oci8_base_t *base = DATA_PTR(self);
422
+ OCINumber onum;
422
423
  union {
423
- OCINumber *value;
424
+ void *value;
424
425
  ub8 dummy; /* padding for incorrect attrtype to protect the stack */
425
426
  } v;
426
427
  ub4 size = 0;
@@ -428,7 +429,11 @@ static VALUE attr_get_integer(VALUE self, VALUE attr_type)
428
429
  v.dummy = 0;
429
430
  Check_Type(attr_type, T_FIXNUM);
430
431
  chker2(OCIAttrGet(base->hp.ptr, base->type, &v.value, &size, FIX2INT(attr_type), oci8_errhp), base);
431
- return oci8_make_integer(v.value, oci8_errhp);
432
+
433
+ memset(&onum, 0, sizeof(onum));
434
+ onum.OCINumberPart[0] = size;
435
+ memcpy(&onum.OCINumberPart[1], v.value, size);
436
+ return oci8_make_integer(&onum, oci8_errhp);
432
437
  }
433
438
 
434
439
  /*
@@ -32,7 +32,7 @@ static ID id_denominator;
32
32
  static ID id_Rational;
33
33
  static ID id_BigDecimal;
34
34
 
35
- #ifndef T_RATIONAL
35
+ #ifndef rb_Rational2
36
36
  static VALUE cRational;
37
37
  #endif
38
38
  static VALUE cBigDecimal;
@@ -51,7 +51,7 @@ static OCINumber const_mPI2; /* -PI/2 */
51
51
  #endif
52
52
  #define RBOCI8_T_ORANUMBER (T_MASK + 1)
53
53
  #define RBOCI8_T_BIGDECIMAL (T_MASK + 2)
54
- #ifdef T_RATIONAL
54
+ #ifdef rb_Rational2
55
55
  #define RBOCI8_T_RATIONAL T_RATIONAL
56
56
  #else
57
57
  #define RBOCI8_T_RATIONAL (T_MASK + 3)
@@ -63,7 +63,7 @@ static int rboci8_type(VALUE obj)
63
63
  VALUE klass;
64
64
 
65
65
  switch (type) {
66
- #ifndef T_RATIONAL
66
+ #ifndef rb_Rational2
67
67
  case T_OBJECT:
68
68
  klass = CLASS_OF(obj);
69
69
  if (cRational != 0) {
@@ -1262,7 +1262,7 @@ static VALUE onum_round(int argc, VALUE *argv, VALUE self)
1262
1262
  *
1263
1263
  * @param [Integer]
1264
1264
  * @return [OraNumber]
1265
- * @todo returns {Integer} when <i>decplace</i> is not specified.
1265
+ * @todo returns <i>Integer</i> when <i>decplace</i> is not specified.
1266
1266
  */
1267
1267
  static VALUE onum_trunc(int argc, VALUE *argv, VALUE self)
1268
1268
  {
@@ -1435,7 +1435,7 @@ static VALUE onum_to_r(VALUE self)
1435
1435
  } else {
1436
1436
  y = rb_funcall(INT2FIX(10), rb_intern("**"), 1, INT2FIX(nshift));
1437
1437
  }
1438
- #ifdef T_RATIONAL
1438
+ #ifdef rb_Rational2
1439
1439
  return rb_Rational(x, y);
1440
1440
  #else
1441
1441
  if (!cRational) {
@@ -3,29 +3,11 @@
3
3
  * stmt.c - part of ruby-oci8
4
4
  * implement the methods of OCIStmt.
5
5
  *
6
- * Copyright (C) 2002-2010 KUBO Takehiro <kubo@jiubao.org>
6
+ * Copyright (C) 2002-2012 KUBO Takehiro <kubo@jiubao.org>
7
7
  *
8
8
  */
9
9
  #include "oci8.h"
10
10
 
11
- static VALUE oci8_sym_select_stmt;
12
- static VALUE oci8_sym_update_stmt;
13
- static VALUE oci8_sym_delete_stmt;
14
- static VALUE oci8_sym_insert_stmt;
15
- static VALUE oci8_sym_create_stmt;
16
- static VALUE oci8_sym_drop_stmt;
17
- static VALUE oci8_sym_alter_stmt;
18
- static VALUE oci8_sym_begin_stmt;
19
- static VALUE oci8_sym_declare_stmt;
20
- static ID id_at_column_metadata;
21
- static ID id_at_actual_array_size;
22
- static ID id_at_max_array_size;
23
- static ID id_each_value;
24
- static ID id_at_names;
25
- static ID id_empty_p;
26
- static ID id_at_con;
27
- static ID id_clear;
28
-
29
11
  VALUE cOCIStmt;
30
12
 
31
13
  #define TO_STMT(obj) ((oci8_stmt_t *)oci8_get_handle((obj), cOCIStmt))
@@ -33,8 +15,6 @@ VALUE cOCIStmt;
33
15
  typedef struct {
34
16
  oci8_base_t base;
35
17
  VALUE svc;
36
- VALUE binds;
37
- VALUE defns;
38
18
  int use_stmt_release;
39
19
  } oci8_stmt_t;
40
20
 
@@ -42,16 +22,12 @@ static void oci8_stmt_mark(oci8_base_t *base)
42
22
  {
43
23
  oci8_stmt_t *stmt = (oci8_stmt_t *)base;
44
24
  rb_gc_mark(stmt->svc);
45
- rb_gc_mark(stmt->binds);
46
- rb_gc_mark(stmt->defns);
47
25
  }
48
26
 
49
27
  static void oci8_stmt_free(oci8_base_t *base)
50
28
  {
51
29
  oci8_stmt_t *stmt = (oci8_stmt_t *)base;
52
30
  stmt->svc = Qnil;
53
- stmt->binds = Qnil;
54
- stmt->defns = Qnil;
55
31
  if (stmt->use_stmt_release) {
56
32
  OCIStmtRelease(base->hp.stmt, oci8_errhp, NULL, 0, OCI_DEFAULT);
57
33
  base->type = 0;
@@ -65,19 +41,24 @@ static oci8_base_vtable_t oci8_stmt_vtable = {
65
41
  sizeof(oci8_stmt_t),
66
42
  };
67
43
 
68
- static VALUE oci8_stmt_initialize(int argc, VALUE *argv, VALUE self)
44
+ /*
45
+ * call-seq:
46
+ * __initialize(connection, sql_statement)
47
+ *
48
+ * @param [OCI8] connection
49
+ * @param [String] sql_statement
50
+ *
51
+ * @private
52
+ */
53
+ static VALUE oci8_stmt_initialize(VALUE self, VALUE svc, VALUE sql)
69
54
  {
70
55
  oci8_stmt_t *stmt = DATA_PTR(self);
71
56
  oci8_svcctx_t *svcctx;
72
- VALUE svc;
73
- VALUE sql;
74
57
  sword rv;
75
58
 
76
- rb_scan_args(argc, argv, "11", &svc, &sql);
77
-
78
59
  svcctx = oci8_get_svcctx(svc);
79
60
  oci8_check_pid_consistency(svcctx);
80
- if (argc > 1 && oracle_client_version >= ORAVER_9_2) {
61
+ if (!NIL_P(sql) && oracle_client_version >= ORAVER_9_2) {
81
62
  OCI8SafeStringValue(sql);
82
63
 
83
64
  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);
@@ -92,7 +73,7 @@ static VALUE oci8_stmt_initialize(int argc, VALUE *argv, VALUE self)
92
73
  oci8_env_raise(oci8_envhp, rv);
93
74
  }
94
75
  stmt->base.type = OCI_HTYPE_STMT;
95
- if (argc > 1) {
76
+ if (!NIL_P(sql)) {
96
77
  OCI8SafeStringValue(sql);
97
78
  rv = OCIStmtPrepare(stmt->base.hp.stmt, oci8_errhp, RSTRING_ORATEXT(sql), RSTRING_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
98
79
  if (IS_OCI_ERROR(rv)) {
@@ -101,17 +82,20 @@ static VALUE oci8_stmt_initialize(int argc, VALUE *argv, VALUE self)
101
82
  }
102
83
  }
103
84
  stmt->svc = svc;
104
- stmt->binds = rb_hash_new();
105
- stmt->defns = rb_ary_new();
106
- rb_ivar_set(stmt->base.self, id_at_column_metadata, rb_ary_new());
107
- rb_ivar_set(stmt->base.self, id_at_names, Qnil);
108
- rb_ivar_set(stmt->base.self, id_at_con, svc);
109
- rb_ivar_set(stmt->base.self, id_at_max_array_size, Qnil);
110
85
 
111
86
  oci8_link_to_parent((oci8_base_t*)stmt, (oci8_base_t*)DATA_PTR(svc));
112
87
  return Qnil;
113
88
  }
114
89
 
90
+ /*
91
+ * call-seq:
92
+ * __define(position, bindobj)
93
+ *
94
+ * @param [Integer] position
95
+ * @param [OCI8::BindType::Base] bindobj
96
+ *
97
+ * @private
98
+ */
115
99
  static VALUE oci8_define_by_pos(VALUE self, VALUE vposition, VALUE vbindobj)
116
100
  {
117
101
  oci8_stmt_t *stmt = TO_STMT(self);
@@ -141,16 +125,18 @@ static VALUE oci8_define_by_pos(VALUE self, VALUE vposition, VALUE vbindobj)
141
125
  if (vptr->post_bind_hook != NULL) {
142
126
  vptr->post_bind_hook(obind);
143
127
  }
144
- if (position - 1 < RARRAY_LEN(stmt->defns)) {
145
- VALUE old_value = RARRAY_PTR(stmt->defns)[position - 1];
146
- if (!NIL_P(old_value)) {
147
- oci8_base_free((oci8_base_t*)oci8_get_bind(old_value));
148
- }
149
- }
150
- rb_ary_store(stmt->defns, position - 1, obind->base.self);
151
128
  return obind->base.self;
152
129
  }
153
130
 
131
+ /*
132
+ * call-seq:
133
+ * __bind(placeholder, bindobj)
134
+ *
135
+ * @param [Integer, String or Symbol] placeholder
136
+ * @param [OCI8::BindType::Base] bindobj
137
+ *
138
+ * @private
139
+ */
154
140
  static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
155
141
  {
156
142
  oci8_stmt_t *stmt = TO_STMT(self);
@@ -160,7 +146,6 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
160
146
  oci8_bind_t *obind;
161
147
  const oci8_bind_vtable_t *vptr;
162
148
  sword status;
163
- VALUE old_value;
164
149
  void *indp;
165
150
 
166
151
  if (NIL_P(vplaceholder)) { /* 1 */
@@ -207,11 +192,6 @@ static VALUE oci8_bind(VALUE self, VALUE vplaceholder, VALUE vbindobj)
207
192
  if (vptr->post_bind_hook != NULL) {
208
193
  vptr->post_bind_hook(obind);
209
194
  }
210
- old_value = rb_hash_aref(stmt->binds, vplaceholder);
211
- if (!NIL_P(old_value)) {
212
- oci8_base_free((oci8_base_t*)oci8_get_bind(old_value));
213
- }
214
- rb_hash_aset(stmt->binds, vplaceholder, obind->base.self);
215
195
  return obind->base.self;
216
196
  }
217
197
 
@@ -232,59 +212,39 @@ static sword oci8_call_stmt_execute(oci8_svcctx_t *svcctx, oci8_stmt_t *stmt, ub
232
212
  return rv;
233
213
  }
234
214
 
215
+ /*
216
+ * call-seq:
217
+ * __execute(iteration_count)
218
+ *
219
+ * @param [Integer] iteration_count
220
+ *
221
+ * @private
222
+ */
235
223
  static VALUE oci8_stmt_execute(VALUE self, VALUE iteration_count)
236
224
  {
237
225
  oci8_stmt_t *stmt = TO_STMT(self);
238
226
  oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
239
- ub4 iters;
240
- ub4 mode;
241
227
 
242
- if (oci8_get_ub2_attr(&stmt->base, OCI_ATTR_STMT_TYPE, stmt->base.hp.stmt) == INT2FIX(OCI_STMT_SELECT)) {
243
- iters = 0;
244
- mode = OCI_DEFAULT;
245
- } else {
246
- if(!NIL_P(iteration_count))
247
- iters = NUM2INT(iteration_count);
248
- else
249
- iters = 1;
250
- mode = svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT;
251
- }
252
- chker3(oci8_call_stmt_execute(svcctx, stmt, iters, mode),
228
+ chker3(oci8_call_stmt_execute(svcctx, stmt, NUM2UINT(iteration_count),
229
+ svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT),
253
230
  &stmt->base, stmt->base.hp.stmt);
254
231
  return self;
255
232
  }
256
233
 
257
- static VALUE each_value(VALUE obj)
258
- {
259
- return rb_funcall(obj, id_each_value, 0);
260
- }
261
-
262
- static VALUE clear_binds_iterator_proc(VALUE val, VALUE arg)
263
- {
264
- if(!NIL_P(val)) {
265
- oci8_base_free((oci8_base_t*)oci8_get_bind(val));
266
- }
267
- return Qnil;
268
- }
269
-
270
- static VALUE oci8_stmt_clear_binds(VALUE self)
234
+ /*
235
+ * call-seq:
236
+ * __fetch(connection)
237
+ *
238
+ * @param [OCI8] connection
239
+ * @return [true or false] true on success and false when all rows are fetched.
240
+ *
241
+ * @private
242
+ */
243
+ static VALUE oci8_stmt_fetch(VALUE self, VALUE svc)
271
244
  {
272
245
  oci8_stmt_t *stmt = TO_STMT(self);
273
-
274
- if(!RTEST(rb_funcall(stmt->binds, id_empty_p, 0)))
275
- {
276
- rb_iterate(each_value, stmt->binds, clear_binds_iterator_proc, Qnil);
277
- rb_funcall(stmt->binds,id_clear,0);
278
- }
279
-
280
- return self;
281
- }
282
-
283
- static VALUE oci8_stmt_do_fetch(oci8_stmt_t *stmt, oci8_svcctx_t *svcctx)
284
- {
285
- VALUE ary;
246
+ oci8_svcctx_t *svcctx = oci8_get_svcctx(svc);
286
247
  sword rv;
287
- long idx;
288
248
  oci8_bind_t *obind;
289
249
  const oci8_bind_vtable_t *vptr;
290
250
 
@@ -302,46 +262,21 @@ static VALUE oci8_stmt_do_fetch(oci8_stmt_t *stmt, oci8_svcctx_t *svcctx)
302
262
  }
303
263
  rv = OCIStmtFetch_nb(svcctx, stmt->base.hp.stmt, oci8_errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
304
264
  if (rv == OCI_NO_DATA) {
305
- return Qnil;
265
+ return Qfalse;
306
266
  }
307
267
  chker3(rv, &svcctx->base, stmt->base.hp.stmt);
308
- ary = rb_ary_new2(RARRAY_LEN(stmt->defns));
309
- for (idx = 0; idx < RARRAY_LEN(stmt->defns); idx++) {
310
- rb_ary_store(ary, idx, oci8_bind_get_data(RARRAY_PTR(stmt->defns)[idx]));
311
- }
312
- return ary;
268
+ return Qtrue;
313
269
  }
314
270
 
315
271
  /*
316
- * Gets fetched data as array. This is available for select
317
- * statement only.
272
+ * call-seq:
273
+ * __paramGet(pos)
274
+ *
275
+ * @param [Fixnum] pos Column position which starts from one
276
+ * @return [OCI8::Metadata::Base]
318
277
  *
319
- * example:
320
- * conn = OCI8.new('scott', 'tiger')
321
- * cursor = conn.exec('SELECT * FROM emp')
322
- * while r = cursor.fetch()
323
- * puts r.join(',')
324
- * end
325
- * cursor.close
326
- * conn.logoff
278
+ * @private
327
279
  */
328
- static VALUE oci8_stmt_fetch(VALUE self)
329
- {
330
- oci8_stmt_t *stmt = TO_STMT(self);
331
- oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
332
-
333
- if (rb_block_given_p()) {
334
- for (;;) {
335
- VALUE rs = oci8_stmt_do_fetch(stmt, svcctx);
336
- if (NIL_P(rs))
337
- return self; /* NEED TO CHECK 0.1 behavior. */
338
- rb_yield(rs);
339
- }
340
- } else {
341
- return oci8_stmt_do_fetch(stmt, svcctx);
342
- }
343
- }
344
-
345
280
  static VALUE oci8_stmt_get_param(VALUE self, VALUE pos)
346
281
  {
347
282
  oci8_stmt_t *stmt = TO_STMT(self);
@@ -357,73 +292,15 @@ static VALUE oci8_stmt_get_param(VALUE self, VALUE pos)
357
292
  }
358
293
 
359
294
  /*
360
- * gets the type of SQL statement as follows.
361
- * * OCI8::STMT_SELECT
362
- * * OCI8::STMT_UPDATE
363
- * * OCI8::STMT_DELETE
364
- * * OCI8::STMT_INSERT
365
- * * OCI8::STMT_CREATE
366
- * * OCI8::STMT_DROP
367
- * * OCI8::STMT_ALTER
368
- * * OCI8::STMT_BEGIN (PL/SQL block which starts with a BEGIN keyword)
369
- * * OCI8::STMT_DECLARE (PL/SQL block which starts with a DECLARE keyword)
370
- * * Other Fixnum value undocumented in Oracle manuals.
371
- *
372
- * <em>Changes between ruby-oci8 1.0 and 2.0.</em>
373
- *
374
- * [ruby-oci8 2.0] OCI8::STMT_* are Symbols. (:select_stmt, :update_stmt, etc.)
375
- * [ruby-oci8 1.0] OCI8::STMT_* are Fixnums. (1, 2, 3, etc.)
376
- */
377
- static VALUE oci8_stmt_get_stmt_type(VALUE self)
378
- {
379
- oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
380
- VALUE stmt_type = oci8_get_ub2_attr(base, OCI_ATTR_STMT_TYPE, base->hp.stmt);
381
- switch (FIX2INT(stmt_type)) {
382
- case OCI_STMT_SELECT:
383
- return oci8_sym_select_stmt;
384
- case OCI_STMT_UPDATE:
385
- return oci8_sym_update_stmt;
386
- case OCI_STMT_DELETE:
387
- return oci8_sym_delete_stmt;
388
- case OCI_STMT_INSERT:
389
- return oci8_sym_insert_stmt;
390
- case OCI_STMT_CREATE:
391
- return oci8_sym_create_stmt;
392
- case OCI_STMT_DROP:
393
- return oci8_sym_drop_stmt;
394
- case OCI_STMT_ALTER:
395
- return oci8_sym_alter_stmt;
396
- case OCI_STMT_BEGIN:
397
- return oci8_sym_begin_stmt;
398
- case OCI_STMT_DECLARE:
399
- return oci8_sym_declare_stmt;
400
- default:
401
- return stmt_type;
402
- }
403
- }
404
-
405
- /*
406
- * Returns the number of processed rows.
407
- */
408
- static VALUE oci8_stmt_get_row_count(VALUE self)
409
- {
410
- oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
411
- return oci8_get_ub4_attr(base, OCI_ATTR_ROW_COUNT, base->hp.stmt);
412
- }
413
-
414
- /*
415
- * Get the rowid of the last inserted/updated/deleted row.
295
+ * Get the rowid of the last inserted, updated or deleted row.
416
296
  * This cannot be used for select statements.
417
297
  *
418
- * example:
298
+ * @example
419
299
  * cursor = conn.parse('INSERT INTO foo_table values(:1, :2)', 1, 2)
420
300
  * cursor.exec
421
- * cursor.rowid # => the inserted row's rowid
301
+ * cursor.rowid # => "AAAFlLAAEAAAAG9AAA", the inserted row's rowid
422
302
  *
423
- * <em>Changes between ruby-oci8 1.0.3 and 1.0.4.</em>
424
- *
425
- * [ruby-oci8 1.0.4 or upper] The return value is a String.
426
- * [ruby-oci8 1.0.3 or lower] It returns an OCIRowid object.
303
+ * @return [String]
427
304
  */
428
305
  static VALUE oci8_stmt_get_rowid(VALUE self)
429
306
  {
@@ -431,158 +308,6 @@ static VALUE oci8_stmt_get_rowid(VALUE self)
431
308
  return oci8_get_rowid_attr(base, OCI_ATTR_ROWID, base->hp.stmt);
432
309
  }
433
310
 
434
- static VALUE oci8_stmt_get_param_count(VALUE self)
435
- {
436
- oci8_base_t *base = oci8_get_handle(self, cOCIStmt);
437
- return oci8_get_ub4_attr(base, OCI_ATTR_PARAM_COUNT, base->hp.stmt);
438
- }
439
-
440
- /*
441
- * call-seq:
442
- * [key]
443
- *
444
- * Gets the value of the bind variable.
445
- *
446
- * In case of binding explicitly, use same key with that of
447
- * OCI8::Cursor#bind_param. A placeholder can be bound by
448
- * name or position. If you bind by name, use that name. If you bind
449
- * by position, use the position.
450
- *
451
- * example:
452
- * cursor = conn.parse("BEGIN :out := 'BAR'; END;")
453
- * cursor.bind_param(':out', 'FOO') # bind by name
454
- * p cursor[':out'] # => 'FOO'
455
- * p cursor[1] # => nil
456
- * cursor.exec()
457
- * p cursor[':out'] # => 'BAR'
458
- * p cursor[1] # => nil
459
- *
460
- * example:
461
- * cursor = conn.parse("BEGIN :out := 'BAR'; END;")
462
- * cursor.bind_param(1, 'FOO') # bind by position
463
- * p cursor[':out'] # => nil
464
- * p cursor[1] # => 'FOO'
465
- * cursor.exec()
466
- * p cursor[':out'] # => nil
467
- * p cursor[1] # => 'BAR'
468
- *
469
- * In case of binding by OCI8#exec or OCI8::Cursor#exec,
470
- * get the value by position, which starts from 1.
471
- *
472
- * example:
473
- * cursor = conn.exec("BEGIN :out := 'BAR'; END;", 'FOO')
474
- * # 1st bind variable is bound as String with width 3. Its initial value is 'FOO'
475
- * # After execute, the value become 'BAR'.
476
- * p cursor[1] # => 'BAR'
477
- */
478
- static VALUE oci8_stmt_aref(VALUE self, VALUE key)
479
- {
480
- oci8_stmt_t *stmt = TO_STMT(self);
481
- VALUE obj = rb_hash_aref(stmt->binds, key);
482
- if (NIL_P(obj)) {
483
- return Qnil;
484
- }
485
- return oci8_bind_get_data(obj);
486
- }
487
-
488
- /*
489
- * call-seq:
490
- * [key] = val
491
- *
492
- * Sets the value to the bind variable. The way to specify the
493
- * +key+ is same with OCI8::Cursor#[]. This is available
494
- * to replace the value and execute many times.
495
- *
496
- * example1:
497
- * cursor = conn.parse("INSERT INTO test(col1) VALUES(:1)")
498
- * cursor.bind_params(1, nil, String, 3)
499
- * ['FOO', 'BAR', 'BAZ'].each do |key|
500
- * cursor[1] = key
501
- * cursor.exec
502
- * end
503
- * cursor.close()
504
- *
505
- * example2:
506
- * ['FOO', 'BAR', 'BAZ'].each do |key|
507
- * conn.exec("INSERT INTO test(col1) VALUES(:1)", key)
508
- * end
509
- *
510
- * Both example's results are same. But the former will use less resources.
511
- */
512
- static VALUE oci8_stmt_aset(VALUE self, VALUE key, VALUE val)
513
- {
514
- long max_array_size;
515
- long actual_array_size;
516
- long bind_array_size;
517
-
518
- oci8_stmt_t *stmt = TO_STMT(self);
519
- VALUE obj = rb_hash_aref(stmt->binds, key);
520
- if (NIL_P(obj)) {
521
- return Qnil; /* ?? MUST BE ERROR? */
522
- }
523
-
524
- if(TYPE(val) == T_ARRAY) {
525
- max_array_size = NUM2INT(rb_ivar_get(self, id_at_max_array_size));
526
- actual_array_size = NUM2INT(rb_ivar_get(self, id_at_actual_array_size));
527
- bind_array_size = RARRAY_LEN(val);
528
-
529
- if(actual_array_size > 0 && bind_array_size != actual_array_size) {
530
- rb_raise(rb_eRuntimeError, "all binding arrays hould be the same size");
531
- }
532
- if(bind_array_size <= max_array_size && actual_array_size == 0) {
533
- rb_ivar_set(self, id_at_actual_array_size, INT2NUM(bind_array_size));
534
- }
535
- }
536
- oci8_bind_set_data(obj, val);
537
- return val;
538
- }
539
-
540
- /*
541
- * call-seq:
542
- * keys -> an Array
543
- *
544
- * Returns the keys of bind variables as array.
545
- */
546
- static VALUE oci8_stmt_keys(VALUE self)
547
- {
548
- oci8_stmt_t *stmt = TO_STMT(self);
549
- return rb_funcall(stmt->binds, oci8_id_keys, 0);
550
- }
551
-
552
- static VALUE oci8_stmt_defined_p(VALUE self, VALUE pos)
553
- {
554
- oci8_stmt_t *stmt = TO_STMT(self);
555
- long position = NUM2INT(pos);
556
-
557
- if (position - 1 < RARRAY_LEN(stmt->defns)) {
558
- VALUE value = RARRAY_PTR(stmt->defns)[position - 1];
559
- if (!NIL_P(value)) {
560
- return Qtrue;
561
- }
562
- }
563
- return Qfalse;
564
- }
565
-
566
- /*
567
- * call-seq:
568
- * prefetch_rows = aFixnum
569
- *
570
- * Set number of rows to be prefetched.
571
- * This can reduce the number of network round trips when fetching
572
- * many rows. The default value is one.
573
- *
574
- * FYI: Rails oracle adaptor uses 100 by default.
575
- */
576
- static VALUE oci8_stmt_set_prefetch_rows(VALUE self, VALUE rows)
577
- {
578
- oci8_stmt_t *stmt = TO_STMT(self);
579
- ub4 num = NUM2UINT(rows);
580
-
581
- chker2(OCIAttrSet(stmt->base.hp.ptr, OCI_HTYPE_STMT, &num, 0, OCI_ATTR_PREFETCH_ROWS, oci8_errhp),
582
- &stmt->base);
583
- return Qfalse;
584
- }
585
-
586
311
  /*
587
312
  * bind_stmt
588
313
  */
@@ -646,40 +371,13 @@ void Init_oci8_stmt(VALUE cOCI8)
646
371
  #endif
647
372
  cOCIStmt = oci8_define_class_under(cOCI8, "Cursor", &oci8_stmt_vtable);
648
373
 
649
- oci8_sym_select_stmt = ID2SYM(rb_intern("select_stmt"));
650
- oci8_sym_update_stmt = ID2SYM(rb_intern("update_stmt"));
651
- oci8_sym_delete_stmt = ID2SYM(rb_intern("delete_stmt"));
652
- oci8_sym_insert_stmt = ID2SYM(rb_intern("insert_stmt"));
653
- oci8_sym_create_stmt = ID2SYM(rb_intern("create_stmt"));
654
- oci8_sym_drop_stmt = ID2SYM(rb_intern("drop_stmt"));
655
- oci8_sym_alter_stmt = ID2SYM(rb_intern("alter_stmt"));
656
- oci8_sym_begin_stmt = ID2SYM(rb_intern("begin_stmt"));
657
- oci8_sym_declare_stmt = ID2SYM(rb_intern("declare_stmt"));
658
- id_at_column_metadata = rb_intern("@column_metadata");
659
- id_at_actual_array_size = rb_intern("@actual_array_size");
660
- id_at_max_array_size = rb_intern("@max_array_size");
661
- id_each_value = rb_intern("each_value");
662
- id_at_names = rb_intern("@names");
663
- id_at_con = rb_intern("@con");
664
- id_empty_p = rb_intern("empty?");
665
- id_clear = rb_intern("clear");
666
-
667
- rb_define_private_method(cOCIStmt, "initialize", oci8_stmt_initialize, -1);
374
+ rb_define_private_method(cOCIStmt, "__initialize", oci8_stmt_initialize, 2);
668
375
  rb_define_private_method(cOCIStmt, "__define", oci8_define_by_pos, 2);
669
376
  rb_define_private_method(cOCIStmt, "__bind", oci8_bind, 2);
670
377
  rb_define_private_method(cOCIStmt, "__execute", oci8_stmt_execute, 1);
671
- rb_define_private_method(cOCIStmt, "__clearBinds", oci8_stmt_clear_binds, 0);
672
- rb_define_method(cOCIStmt, "fetch", oci8_stmt_fetch, 0);
378
+ rb_define_private_method(cOCIStmt, "__fetch", oci8_stmt_fetch, 1);
673
379
  rb_define_private_method(cOCIStmt, "__paramGet", oci8_stmt_get_param, 1);
674
- rb_define_method(cOCIStmt, "type", oci8_stmt_get_stmt_type, 0);
675
- rb_define_method(cOCIStmt, "row_count", oci8_stmt_get_row_count, 0);
676
380
  rb_define_method(cOCIStmt, "rowid", oci8_stmt_get_rowid, 0);
677
- rb_define_private_method(cOCIStmt, "__param_count", oci8_stmt_get_param_count, 0);
678
- rb_define_method(cOCIStmt, "[]", oci8_stmt_aref, 1);
679
- rb_define_method(cOCIStmt, "[]=", oci8_stmt_aset, 2);
680
- rb_define_method(cOCIStmt, "keys", oci8_stmt_keys, 0);
681
- rb_define_private_method(cOCIStmt, "__defined?", oci8_stmt_defined_p, 1);
682
- rb_define_method(cOCIStmt, "prefetch_rows=", oci8_stmt_set_prefetch_rows, 1);
683
381
 
684
382
  oci8_define_bind_class("Cursor", &bind_stmt_vtable);
685
383
  }