ruby-oci8 2.0.4 → 2.0.5

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.
@@ -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);