ruby-oci8 2.0.4 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@
3
3
  * attr.c
4
4
  *
5
5
  * $Author: kubo $
6
- * $Date: 2009-05-17 19:08:39 +0900 (Sun, 17 May 2009) $
6
+ * $Date: 2009-05-17 19:08:39 +0900 (日, 17 5月 2009) $
7
7
  *
8
8
  * Copyright (C) 2002-2007 KUBO Takehiro <kubo@jiubao.org>
9
9
  */
@@ -3,7 +3,7 @@
3
3
  * bind.c
4
4
  *
5
5
  * $Author: kubo $
6
- * $Date: 2009-05-19 21:51:28 +0900 (Tue, 19 May 2009) $
6
+ * $Date: 2010-12-14 20:30:44 +0900 (火, 14 12月 2010) $
7
7
  *
8
8
  * Copyright (C) 2002-2008 KUBO Takehiro <kubo@jiubao.org>
9
9
  */
@@ -14,7 +14,7 @@ static ID id_bind_type;
14
14
  static VALUE cOCI8BindTypeBase;
15
15
 
16
16
  /*
17
- * bind_string
17
+ * bind_char and bind_nchar
18
18
  */
19
19
  static VALUE bind_string_get(oci8_bind_t *obind, void *data, void *null_struct)
20
20
  {
@@ -54,7 +54,7 @@ static void bind_string_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE len
54
54
  obind->alloc_sz = (sz + (sizeof(sb4) - 1)) & ~(sizeof(sb4) - 1);
55
55
  }
56
56
 
57
- static const oci8_bind_class_t bind_string_class = {
57
+ static const oci8_bind_class_t bind_char_class = {
58
58
  {
59
59
  NULL,
60
60
  oci8_bind_free,
@@ -70,6 +70,23 @@ static const oci8_bind_class_t bind_string_class = {
70
70
  SQLT_LVC
71
71
  };
72
72
 
73
+ static const oci8_bind_class_t bind_nchar_class = {
74
+ {
75
+ NULL,
76
+ oci8_bind_free,
77
+ sizeof(oci8_bind_t)
78
+ },
79
+ bind_string_get,
80
+ bind_string_set,
81
+ bind_string_init,
82
+ NULL,
83
+ NULL,
84
+ NULL,
85
+ NULL,
86
+ SQLT_LVC,
87
+ SQLCS_NCHAR,
88
+ };
89
+
73
90
  /*
74
91
  * bind_raw
75
92
  */
@@ -79,6 +96,18 @@ static VALUE bind_raw_get(oci8_bind_t *obind, void *data, void *null_struct)
79
96
  return rb_str_new(vstr->buf, vstr->size);
80
97
  }
81
98
 
99
+ static void bind_raw_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
100
+ {
101
+ oci8_vstr_t *vstr = (oci8_vstr_t *)data;
102
+
103
+ StringValue(val);
104
+ if (RSTRING_LEN(val) > obind->value_sz - sizeof(vstr->size)) {
105
+ rb_raise(rb_eArgError, "too long String to set. (%ld for %d)", RSTRING_LEN(val), obind->value_sz - (sb4)sizeof(vstr->size));
106
+ }
107
+ memcpy(vstr->buf, RSTRING_PTR(val), RSTRING_LEN(val));
108
+ vstr->size = RSTRING_LEN(val);
109
+ }
110
+
82
111
  static const oci8_bind_class_t bind_raw_class = {
83
112
  {
84
113
  NULL,
@@ -86,7 +115,7 @@ static const oci8_bind_class_t bind_raw_class = {
86
115
  sizeof(oci8_bind_t)
87
116
  },
88
117
  bind_raw_get,
89
- bind_string_set,
118
+ bind_raw_set,
90
119
  bind_string_init,
91
120
  NULL,
92
121
  NULL,
@@ -303,10 +332,6 @@ static VALUE oci8_bind_get(VALUE self)
303
332
  if (NIL_P(obind->tdo)) {
304
333
  if (obind->u.inds[idx] != 0)
305
334
  return Qnil;
306
- } else {
307
- null_structp = &obind->u.null_structs[idx];
308
- if (*(OCIInd*)*null_structp != 0)
309
- return Qnil;
310
335
  }
311
336
  return obc->get(obind, (void*)((size_t)obind->valuep + obind->alloc_sz * idx), null_structp);
312
337
  }
@@ -350,6 +375,7 @@ static VALUE oci8_bind_set(VALUE self, VALUE val)
350
375
  obind->u.inds[idx] = 0;
351
376
  } else {
352
377
  null_structp = &obind->u.null_structs[idx];
378
+ *(OCIInd*)obind->u.null_structs[idx] = 0;
353
379
  }
354
380
  obc->set(obind, (void*)((size_t)obind->valuep + obind->alloc_sz * idx), null_structp, val);
355
381
  }
@@ -451,7 +477,8 @@ void Init_oci8_bind(VALUE klass)
451
477
  rb_define_method(cOCI8BindTypeBase, "set", oci8_bind_set, 1);
452
478
 
453
479
  /* register primitive data types. */
454
- oci8_define_bind_class("String", &bind_string_class);
480
+ oci8_define_bind_class("CHAR", &bind_char_class);
481
+ oci8_define_bind_class("NCHAR", &bind_nchar_class);
455
482
  oci8_define_bind_class("RAW", &bind_raw_class);
456
483
  #ifdef USE_DYNAMIC_FETCH
457
484
  oci8_define_bind_class("Long", &bind_long_class);
@@ -2,28 +2,17 @@
2
2
  /*
3
3
  * env.c - part of ruby-oci8
4
4
  *
5
- * Copyright (C) 2002-2009 KUBO Takehiro <kubo@jiubao.org>
5
+ * Copyright (C) 2002-2011 KUBO Takehiro <kubo@jiubao.org>
6
6
  */
7
7
  #include "oci8.h"
8
8
 
9
- #if !defined(RUBY_VM)
10
- /* ruby_setenv for workaround ruby 1.8.4 */
11
- #include <util.h>
12
- #endif
13
-
14
- #ifdef _WIN32
15
- #ifdef HAVE_RUBY_WIN32_H
16
- #include <ruby/win32.h> /* for rb_w32_getenv() */
17
- #else
18
- #include <win32/win32.h> /* for rb_w32_getenv() */
19
- #endif
20
- #endif
21
-
22
- #ifdef HAVE_RUBY_UTIL_H
9
+ #if defined(HAVE_RUBY_UTIL_H)
23
10
  #include <ruby/util.h>
11
+ #elif defined(HAVE_UTIL_H)
12
+ #include <util.h>
24
13
  #endif
25
14
 
26
- #ifdef RUBY_VM
15
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
27
16
  ub4 oci8_env_mode = OCI_OBJECT | OCI_THREADED;
28
17
  #else
29
18
  ub4 oci8_env_mode = OCI_OBJECT;
@@ -42,23 +31,53 @@ OCIEnv *oci8_make_envhp(void)
42
31
  return oci8_global_envhp;
43
32
  }
44
33
 
45
- #ifdef RUBY_VM
34
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
46
35
  /*
47
36
  * oci8_errhp is a thread local object in ruby 1.9.
48
37
  */
49
38
 
50
39
  oci8_tls_key_t oci8_tls_key; /* native thread key */
51
- static ID id_thread_key; /* ruby's thread key */
52
40
 
53
- static void oci8_free_errhp(OCIError *errhp)
41
+ /* This function is called on the native thread termination
42
+ * if the thread local errhp is not null.
43
+ */
44
+ static void oci8_free_errhp(void *errhp)
54
45
  {
55
46
  OCIHandleFree(errhp, OCI_HTYPE_ERROR);
56
47
  }
57
48
 
49
+ #ifdef _WIN32
50
+ static int dllmain_is_called;
51
+
52
+ __declspec(dllexport)
53
+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
54
+ {
55
+ void *errhp;
56
+
57
+ switch (fdwReason) {
58
+ case DLL_PROCESS_ATTACH:
59
+ dllmain_is_called = 1;
60
+ break;
61
+ case DLL_THREAD_ATTACH:
62
+ /* do nothing */
63
+ break;
64
+ case DLL_THREAD_DETACH:
65
+ errhp = oci8_tls_get(oci8_tls_key);
66
+ if (errhp != NULL) {
67
+ oci8_free_errhp(errhp);
68
+ }
69
+ break;
70
+ case DLL_PROCESS_DETACH:
71
+ /* do nothing */
72
+ break;
73
+ }
74
+ return TRUE;
75
+ }
76
+ #endif
77
+
58
78
  OCIError *oci8_make_errhp(void)
59
79
  {
60
80
  OCIError *errhp;
61
- VALUE obj;
62
81
  sword rv;
63
82
 
64
83
  /* create a new errhp. */
@@ -66,15 +85,9 @@ OCIError *oci8_make_errhp(void)
66
85
  if (rv != OCI_SUCCESS) {
67
86
  oci8_env_raise(oci8_envhp, rv);
68
87
  }
69
- /* create a new ruby object which contains errhp to make
70
- * sure that the errhp is freed when it become unnecessary.
88
+ /* Set the errhp to the thread local storage.
89
+ * It is freed by oci8_free_errhp().
71
90
  */
72
- obj = Data_Wrap_Struct(rb_cObject, NULL, oci8_free_errhp, errhp);
73
- /* set the ruby object to ruby's thread local storage to prevent
74
- * it from being freed while the thread is available.
75
- */
76
- rb_thread_local_aset(rb_thread_current(), id_thread_key, obj);
77
-
78
91
  oci8_tls_set(oci8_tls_key, (void*)errhp);
79
92
  return errhp;
80
93
  }
@@ -97,11 +110,11 @@ OCIError *oci8_make_errhp(void)
97
110
 
98
111
  void Init_oci8_env(void)
99
112
  {
100
- #ifdef RUBY_VM
113
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
101
114
  int error;
102
115
  #endif
103
116
 
104
- #if !defined(RUBY_VM) && !defined(_WIN32)
117
+ #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && !defined(_WIN32)
105
118
  /* workaround code.
106
119
  *
107
120
  * Some instant clients set the environment variables
@@ -147,9 +160,22 @@ void Init_oci8_env(void)
147
160
  }
148
161
  }
149
162
 
150
- #ifdef RUBY_VM
151
- id_thread_key = rb_intern("__oci8_errhp__");
152
- error = oci8_tls_key_init(&oci8_tls_key);
163
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
164
+ /* ruby 1.9 */
165
+ #if defined(_WIN32)
166
+ if (!dllmain_is_called) {
167
+ /* sanity check */
168
+ rb_raise(rb_eRuntimeError, "DllMain is not unexpectedly called. This causes resource leaks.");
169
+ }
170
+ oci8_tls_key = TlsAlloc();
171
+ if (oci8_tls_key == 0xFFFFFFFF) {
172
+ error = GetLastError();
173
+ } else {
174
+ error = 0;
175
+ }
176
+ #else
177
+ error = pthread_key_create(&oci8_tls_key, oci8_free_errhp);
178
+ #endif
153
179
  if (error != 0) {
154
180
  rb_raise(rb_eRuntimeError, "Cannot create thread local key (errno = %d)", error);
155
181
  }
@@ -10,6 +10,10 @@
10
10
  */
11
11
  #include "oci8.h"
12
12
 
13
+ #ifndef DLEXT
14
+ #define DLEXT ".so"
15
+ #endif
16
+
13
17
  /* Exception */
14
18
  VALUE eOCIException;
15
19
  VALUE eOCIBreak;
@@ -105,12 +105,18 @@ have_func("localtime_r")
105
105
  have_header("intern.h")
106
106
  have_header("util.h")
107
107
  # ruby 1.9 headers
108
+ have_header("ruby/util.h")
108
109
  have_type('rb_encoding', ['ruby/ruby.h', 'ruby/encoding.h'])
109
110
 
110
111
  # $! in C API
111
112
  have_var("ruby_errinfo", "ruby.h") # ruby 1.8
112
113
  have_func("rb_errinfo", "ruby.h") # ruby 1.9
113
114
 
115
+ have_type("rb_blocking_function_t", "ruby.h")
116
+ have_func("rb_set_end_proc", "ruby.h")
117
+ have_func("rb_class_superclass", "ruby.h")
118
+ have_func("rb_thread_blocking_region", "ruby.h")
119
+
114
120
  # replace files
115
121
  replace = {
116
122
  'OCI8_CLIENT_VERSION' => oraconf.version,
@@ -120,14 +126,22 @@ replace = {
120
126
  # make ruby script before running create_makefile.
121
127
  replace_keyword(File.dirname(__FILE__) + '/../../lib/oci8.rb.in', '../../lib/oci8.rb', replace)
122
128
 
129
+ so_basename = 'oci8lib_'
130
+ if defined? RUBY_ENGINE and RUBY_ENGINE != 'ruby'
131
+ so_basename += RUBY_ENGINE
132
+ end
133
+
123
134
  # Config::CONFIG["ruby_version"] indicates the ruby API version.
124
135
  # 1.8 - ruby 1.8.x
125
136
  # 1.9.1 - ruby 1.9.1 and 1.9.2
126
137
  # 1.9.x - ruby 1.9.x future version which will break the API compatibility
127
- so_basename = "oci8lib_" + Config::CONFIG["ruby_version"].gsub(/\W/, '')
138
+ so_basename += Config::CONFIG["ruby_version"].gsub(/\W/, '')
128
139
 
129
140
  $defs << "-DInit_oci8lib=Init_#{so_basename}"
130
141
  $defs << "-Doci8lib=#{so_basename}"
142
+ if defined? RUBY_ENGINE and RUBY_ENGINE == 'rbx'
143
+ $defs << "-DCHAR_IS_NOT_A_SHORTCUT_TO_ID"
144
+ end
131
145
 
132
146
  create_header()
133
147
 
@@ -64,15 +64,59 @@ VALUE oci8_make_bfile(oci8_svcctx_t *svcctx, OCILobLocator *s)
64
64
  return oci8_make_lob(cOCI8BFILE, svcctx, s);
65
65
  }
66
66
 
67
+ static void oci8_assign_lob(VALUE klass, oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
68
+ {
69
+ oci8_base_t *base = oci8_get_handle(lob, klass);
70
+ oci_lc(OCILobLocatorAssign_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, base->hp.lob, dest));
71
+ }
72
+
73
+ void oci8_assign_clob(oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
74
+ {
75
+ oci8_assign_lob(cOCI8CLOB, svcctx, lob, dest);
76
+ }
77
+
78
+ void oci8_assign_nclob(oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
79
+ {
80
+ oci8_assign_lob(cOCI8NCLOB, svcctx, lob, dest);
81
+ }
82
+
83
+ void oci8_assign_blob(oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
84
+ {
85
+ oci8_assign_lob(cOCI8BLOB, svcctx, lob, dest);
86
+ }
87
+
88
+ void oci8_assign_bfile(oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
89
+ {
90
+ oci8_assign_lob(cOCI8BFILE, svcctx, lob, dest);
91
+ }
92
+
67
93
  static void oci8_lob_mark(oci8_base_t *base)
68
94
  {
69
95
  oci8_lob_t *lob = (oci8_lob_t *)base;
70
96
  rb_gc_mark(lob->svc);
71
97
  }
72
98
 
99
+ static VALUE free_temp_lob(oci8_lob_t *lob)
100
+ {
101
+ oci8_svcctx_t *svcctx = oci8_get_svcctx(lob->svc);
102
+
103
+ OCILobFreeTemporary_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob);
104
+ return Qnil;
105
+ }
106
+
73
107
  static void oci8_lob_free(oci8_base_t *base)
74
108
  {
75
109
  oci8_lob_t *lob = (oci8_lob_t *)base;
110
+ boolean is_temporary;
111
+
112
+ if (have_OCILobIsTemporary
113
+ && OCILobIsTemporary(oci8_envhp, oci8_errhp, lob->base.hp.lob, &is_temporary) == OCI_SUCCESS
114
+ && is_temporary) {
115
+ /* Exceptions in free_temp_lob() are ignored.
116
+ * oci8_lob_free() is called in GC. It must not raise an exception.
117
+ */
118
+ rb_rescue(free_temp_lob, (VALUE)base, NULL, 0);
119
+ }
76
120
  lob->svc = Qnil;
77
121
  }
78
122
 
@@ -38,7 +38,7 @@ VALUE oci8_metadata_create(OCIParam *parmhp, VALUE svc, VALUE parent)
38
38
  VALUE klass;
39
39
  VALUE obj;
40
40
 
41
- Check_Handle(parent, oci8_cOCIHandle, p);
41
+ p = oci8_get_handle(parent, oci8_cOCIHandle);
42
42
 
43
43
  oci_lc(OCIAttrGet(parmhp, OCI_DTYPE_PARAM, &ptype, &size, OCI_ATTR_PTYPE, oci8_errhp));
44
44
  klass = rb_hash_aref(ptype_to_class, INT2FIX(ptype));
@@ -42,6 +42,10 @@ enum {
42
42
  ATTR_BINARY_FLOAT,
43
43
  ATTR_NAMED_TYPE,
44
44
  ATTR_NAMED_COLLECTION,
45
+ ATTR_CLOB,
46
+ ATTR_NCLOB,
47
+ ATTR_BLOB,
48
+ ATTR_BFILE,
45
49
  };
46
50
 
47
51
  static VALUE get_attribute(VALUE self, VALUE datatype, VALUE typeinfo, void *data, OCIInd *ind);
@@ -198,6 +202,14 @@ static VALUE get_attribute(VALUE self, VALUE datatype, VALUE typeinfo, void *dat
198
202
  rv = rb_funcall(tmp_obj, id_to_value, 0);
199
203
  oci8_unlink_from_parent(&obj->base);
200
204
  return rv;
205
+ case ATTR_CLOB:
206
+ return oci8_make_clob(oci8_get_svcctx(typeinfo), *(OCILobLocator**)data);
207
+ case ATTR_NCLOB:
208
+ return oci8_make_nclob(oci8_get_svcctx(typeinfo), *(OCILobLocator**)data);
209
+ case ATTR_BLOB:
210
+ return oci8_make_blob(oci8_get_svcctx(typeinfo), *(OCILobLocator**)data);
211
+ case ATTR_BFILE:
212
+ return oci8_make_bfile(oci8_get_svcctx(typeinfo), *(OCILobLocator**)data);
201
213
  default:
202
214
  rb_raise(rb_eRuntimeError, "not supported datatype");
203
215
  }
@@ -248,6 +260,25 @@ static VALUE oci8_named_type_set_attribute(VALUE self, VALUE datatype, VALUE typ
248
260
  return Qnil;
249
261
  }
250
262
 
263
+ static VALUE oci8_named_type_null_p(VALUE self)
264
+ {
265
+ void *data;
266
+ OCIInd *ind;
267
+
268
+ oci8_named_type_check_offset(self, INT2FIX(0), INT2FIX(0), sizeof(void*), &data, &ind);
269
+ return *ind ? Qtrue : Qfalse;
270
+ }
271
+
272
+ static VALUE oci8_named_type_set_null(VALUE self, VALUE val)
273
+ {
274
+ void *data;
275
+ OCIInd *ind;
276
+
277
+ oci8_named_type_check_offset(self, INT2FIX(0), INT2FIX(0), sizeof(void*), &data, &ind);
278
+ *ind = RTEST(val) ? -1 : 0;
279
+ return val;
280
+ }
281
+
251
282
  typedef struct {
252
283
  VALUE self;
253
284
  VALUE datatype;
@@ -335,6 +366,11 @@ static VALUE oci8_named_coll_set_coll_element(VALUE self, VALUE datatype, VALUE
335
366
  oci_lc(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_NAMEDCOLLECTION, tdo->hp.tdo, NULL, OCI_DURATION_SESSION, TRUE, &cb_data.data.ptr));
336
367
  oci_lc(OCIObjectGetInd(oci8_envhp, oci8_errhp, cb_data.data.ptr, (dvoid**)&cb_data.indp));
337
368
  break;
369
+ case ATTR_CLOB:
370
+ case ATTR_NCLOB:
371
+ case ATTR_BLOB:
372
+ case ATTR_BFILE:
373
+ rb_raise(rb_eRuntimeError, "Could not set LOB objects to collection types yet.");
338
374
  default:
339
375
  rb_raise(rb_eRuntimeError, "not supported datatype");
340
376
  }
@@ -482,6 +518,18 @@ static void set_attribute(VALUE self, VALUE datatype, VALUE typeinfo, void *data
482
518
  rb_funcall(tmp_obj, id_set_attributes, 1, val);
483
519
  oci8_unlink_from_parent(&obj->base);
484
520
  break;
521
+ case ATTR_CLOB:
522
+ oci8_assign_clob(oci8_get_svcctx(typeinfo), val, (OCILobLocator **)data);
523
+ break;
524
+ case ATTR_NCLOB:
525
+ oci8_assign_nclob(oci8_get_svcctx(typeinfo), val, (OCILobLocator **)data);
526
+ break;
527
+ case ATTR_BLOB:
528
+ oci8_assign_blob(oci8_get_svcctx(typeinfo), val, (OCILobLocator **)data);
529
+ break;
530
+ case ATTR_BFILE:
531
+ oci8_assign_bfile(oci8_get_svcctx(typeinfo), val, (OCILobLocator **)data);
532
+ break;
485
533
  default:
486
534
  rb_raise(rb_eRuntimeError, "not supported datatype");
487
535
  }
@@ -578,6 +626,7 @@ static void bind_named_type_init_elem(oci8_bind_t *obind, VALUE svc)
578
626
 
579
627
  oci_lc(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->base.hp.svc, tc, tdo->hp.tdo, NULL, OCI_DURATION_SESSION, TRUE, (dvoid**)obj->instancep));
580
628
  oci_lc(OCIObjectGetInd(oci8_envhp, oci8_errhp, (dvoid*)*obj->instancep, (dvoid**)obj->null_structp));
629
+ *(OCIInd*)*obj->null_structp = -1;
581
630
  } while (++idx < obind->maxar_sz);
582
631
  }
583
632
 
@@ -615,6 +664,10 @@ void Init_oci_object(VALUE cOCI8)
615
664
  rb_define_const(cOCI8TDO, "ATTR_BINARY_FLOAT", INT2FIX(ATTR_BINARY_FLOAT));
616
665
  rb_define_const(cOCI8TDO, "ATTR_NAMED_TYPE", INT2FIX(ATTR_NAMED_TYPE));
617
666
  rb_define_const(cOCI8TDO, "ATTR_NAMED_COLLECTION", INT2FIX(ATTR_NAMED_COLLECTION));
667
+ rb_define_const(cOCI8TDO, "ATTR_CLOB", INT2FIX(ATTR_CLOB));
668
+ rb_define_const(cOCI8TDO, "ATTR_NCLOB", INT2FIX(ATTR_NCLOB));
669
+ rb_define_const(cOCI8TDO, "ATTR_BLOB", INT2FIX(ATTR_BLOB));
670
+ rb_define_const(cOCI8TDO, "ATTR_BFILE", INT2FIX(ATTR_BFILE));
618
671
  #define ALIGNMENT_OF(type) (size_t)&(((struct {char c; type t;}*)0)->t)
619
672
  rb_define_const(cOCI8TDO, "SIZE_OF_POINTER", INT2FIX(sizeof(void *)));
620
673
  rb_define_const(cOCI8TDO, "ALIGNMENT_OF_POINTER", INT2FIX(ALIGNMENT_OF(void *)));
@@ -633,6 +686,8 @@ void Init_oci_object(VALUE cOCI8)
633
686
  rb_define_method(cOCI8NamedType, "tdo", oci8_named_type_tdo, 0);
634
687
  rb_define_private_method(cOCI8NamedType, "get_attribute", oci8_named_type_get_attribute, 4);
635
688
  rb_define_private_method(cOCI8NamedType, "set_attribute", oci8_named_type_set_attribute, 5);
689
+ rb_define_method(cOCI8NamedType, "null?", oci8_named_type_null_p, 0);
690
+ rb_define_method(cOCI8NamedType, "null=", oci8_named_type_set_null, 1);
636
691
 
637
692
  /* OCI8::NamedCollection */
638
693
  cOCI8NamedCollection = oci8_define_class_under(cOCI8, "NamedCollection", &oci8_named_type_class);