ruby-oci8 2.1.8 → 2.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,12 +5,7 @@
5
5
  * Copyright (C) 2002-2011 KUBO Takehiro <kubo@jiubao.org>
6
6
  */
7
7
  #include "oci8.h"
8
-
9
- #if defined(HAVE_RUBY_UTIL_H)
10
8
  #include <ruby/util.h>
11
- #elif defined(HAVE_UTIL_H)
12
- #include <util.h>
13
- #endif
14
9
 
15
10
  ub4 oci8_env_mode = OCI_OBJECT | OCI_THREADED;
16
11
 
@@ -27,7 +22,6 @@ OCIEnv *oci8_make_envhp(void)
27
22
  return oci8_global_envhp;
28
23
  }
29
24
 
30
- #ifdef USE_THREAD_LOCAL_ERRHP
31
25
  /*
32
26
  * Setup thread-local oci8_errhp.
33
27
  */
@@ -87,48 +81,10 @@ OCIError *oci8_make_errhp(void)
87
81
  oci8_tls_set(oci8_tls_key, (void*)errhp);
88
82
  return errhp;
89
83
  }
90
- #else
91
- /*
92
- * oci8_errhp is global in ruby 1.8 configured without --enable-pthread on Unix.
93
- */
94
- OCIError *oci8_global_errhp;
95
-
96
- OCIError *oci8_make_errhp(void)
97
- {
98
- sword rv;
99
-
100
- rv = OCIHandleAlloc(oci8_envhp, (dvoid *)&oci8_global_errhp, OCI_HTYPE_ERROR, 0, NULL);
101
- if (rv != OCI_SUCCESS)
102
- oci8_env_raise(oci8_envhp, rv);
103
- return oci8_global_errhp;
104
- }
105
- #endif
106
84
 
107
85
  void Init_oci8_env(void)
108
86
  {
109
- #ifdef USE_THREAD_LOCAL_ERRHP
110
87
  int error;
111
- #endif
112
-
113
- #if !defined(NATIVE_THREAD_WITH_GVL) && !defined(_WIN32)
114
- /* workaround code.
115
- *
116
- * Some instant clients set the environment variables
117
- * ORA_NLS_PROFILE33, ORA_NLS10 and ORACLE_HOME if they aren't
118
- * set. It causes problems on some platforms.
119
- */
120
- if (RTEST(rb_eval_string("RUBY_VERSION == \"1.8.4\""))) {
121
- if (getenv("ORA_NLS_PROFILE33") == NULL) {
122
- ruby_setenv("ORA_NLS_PROFILE33", "");
123
- }
124
- if (getenv("ORA_NLS10") == NULL) {
125
- ruby_setenv("ORA_NLS10", "");
126
- }
127
- if (getenv("ORACLE_HOME") == NULL) {
128
- ruby_setenv("ORACLE_HOME", ".");
129
- }
130
- }
131
- #endif /* WIN32 */
132
88
 
133
89
  /* workaround code.
134
90
  *
@@ -156,7 +112,6 @@ void Init_oci8_env(void)
156
112
  }
157
113
  }
158
114
 
159
- #ifdef USE_THREAD_LOCAL_ERRHP
160
115
  #if defined(_WIN32)
161
116
  if (!dllmain_is_called) {
162
117
  /* sanity check */
@@ -174,5 +129,4 @@ void Init_oci8_env(void)
174
129
  if (error != 0) {
175
130
  rb_raise(rb_eRuntimeError, "Cannot create thread local key (errno = %d)", error);
176
131
  }
177
- #endif
178
132
  }
@@ -2,20 +2,10 @@
2
2
  /*
3
3
  error.c - part of ruby-oci8
4
4
 
5
- Copyright (C) 2002-2012 KUBO Takehiro <kubo@jiubao.org>
5
+ Copyright (C) 2002-2015 Kubo Takehiro <kubo@jiubao.org>
6
6
 
7
7
  */
8
- #if defined __linux && !defined(_GNU_SOURCE)
9
- #define _GNU_SOURCE 1
10
- #endif
11
8
  #include "oci8.h"
12
- #ifdef HAVE_DLADDR
13
- #include <dlfcn.h>
14
- #endif
15
- #ifdef __CYGWIN__
16
- #undef boolean
17
- #include <windows.h>
18
- #endif
19
9
 
20
10
  #ifndef DLEXT
21
11
  #define DLEXT ".so"
@@ -227,22 +217,8 @@ void oci8_do_raise_init_error(const char *file, int line)
227
217
  {
228
218
  VALUE msg = rb_usascii_str_new_cstr("OCI Library Initialization Error");
229
219
  VALUE exc;
230
- const char *dll_path = NULL;
231
- #if defined _WIN32 || defined __CYGWIN__
232
- HMODULE hMod = GetModuleHandleA("OCI.DLL");
233
- char buf[MAX_PATH];
234
- if (hMod != NULL) {
235
- if (GetModuleFileName(hMod, buf, sizeof(buf))) {
236
- dll_path = buf;
237
- }
238
- }
239
- #elif defined HAVE_DLADDR
240
- void *addr = dlsym(RTLD_DEFAULT, "OCIEnvCreate");
241
- Dl_info info;
242
- if (addr != NULL && dladdr(addr, &info)) {
243
- dll_path = info.dli_fname;
244
- }
245
- #endif
220
+ const char *dll_path = oci8_dll_path();
221
+
246
222
  if (dll_path != NULL) {
247
223
  msg = rb_str_buf_cat_ascii(msg, " - ");
248
224
  msg = rb_enc_str_buf_cat(msg, dll_path, strlen(dll_path), rb_filesystem_encoding());
@@ -258,12 +234,10 @@ VALUE oci8_get_error_message(ub4 msgno, const char *default_msg)
258
234
  const char *errmsg = NULL;
259
235
  char msgbuf[64];
260
236
 
261
- if (have_OCIMessageGet) {
262
- if (msghp == NULL) {
263
- chkerr(OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_CONST_ORATEXT("rdbms"), TO_CONST_ORATEXT("ora"), OCI_DURATION_PROCESS));
264
- }
265
- errmsg = TO_CHARPTR(OCIMessageGet(msghp, msgno, NULL, 0));
237
+ if (msghp == NULL) {
238
+ chkerr(OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_CONST_ORATEXT("rdbms"), TO_CONST_ORATEXT("ora"), OCI_DURATION_PROCESS));
266
239
  }
240
+ errmsg = TO_CHARPTR(OCIMessageGet(msghp, msgno, NULL, 0));
267
241
  if (errmsg == NULL) {
268
242
  if (default_msg != NULL) {
269
243
  errmsg = default_msg;
@@ -1,3 +1,8 @@
1
+ if RUBY_VERSION < "1.9"
2
+ puts "Ruby-oci8 doesn't work ruby 1.8 since ruby-oci8 2.2.0."
3
+ exit 1
4
+ end
5
+
1
6
  begin
2
7
  require 'mkmf'
3
8
  rescue LoadError
@@ -12,24 +17,10 @@ end
12
17
  require File.dirname(__FILE__) + '/oraconf'
13
18
  require File.dirname(__FILE__) + '/apiwrap'
14
19
  require File.dirname(__FILE__) + '/../../lib/oci8/oracle_version.rb'
15
-
16
- RUBY_OCI8_VERSION = File.read("#{File.dirname(__FILE__)}/../../VERSION").chomp
20
+ require File.dirname(__FILE__) + '/../../lib/oci8/version.rb'
17
21
 
18
22
  oraconf = OraConf.get()
19
23
 
20
- def replace_keyword(source, target, replace)
21
- puts "creating #{target} from #{source}"
22
- open(source, "rb") { |f|
23
- buf = f.read
24
- replace.each do |key, value|
25
- buf.gsub!('@@' + key + '@@', value)
26
- end
27
- open(target, "wb") {|fw|
28
- fw.write buf
29
- }
30
- }
31
- end
32
-
33
24
  $CFLAGS += oraconf.cflags
34
25
  saved_libs = $libs
35
26
  $libs += oraconf.libs
@@ -46,6 +37,8 @@ YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml')).each do |key, val|
46
37
  funcs[ver] ||= []
47
38
  funcs[ver] << key
48
39
  end
40
+
41
+ saved_defs = $defs.clone
49
42
  funcs.keys.sort.each do |version|
50
43
  next if version == 0x08000000
51
44
  verstr = format('%d.%d.%d', ((version >> 24) & 0xFF), ((version >> 20) & 0xF), ((version >> 12) & 0xFF))
@@ -62,6 +55,7 @@ funcs.keys.sort.each do |version|
62
55
  puts "checking for Oracle #{verstr} API - #{result}"
63
56
  break if result == 'fail'
64
57
  end
58
+ $defs = saved_defs
65
59
 
66
60
  have_type('oratext', 'ociap.h')
67
61
  have_type('OCIDateTime*', 'ociap.h')
@@ -83,6 +77,25 @@ $defs << "-DORACLE_CLIENT_VERSION=#{format('0x%08x', oci_client_version)}"
83
77
  if with_config('runtime-check')
84
78
  $defs << "-DRUNTIME_API_CHECK=1"
85
79
  $libs = saved_libs
80
+ else
81
+ oraver = OCI8::OracleVersion.new(oci_client_version)
82
+ if oraver < OCI8::OracleVersion.new(10)
83
+ case "#{oraver.major}.#{oraver.minor}"
84
+ when "8.0"
85
+ ora_name = "Oracle 8"
86
+ oci8_ver = "2.0.x"
87
+ when "8.1"
88
+ ora_name = "Oracle 8i"
89
+ oci8_ver = "2.0.x"
90
+ when "9.1"
91
+ ora_name = "Oracle 9iR1"
92
+ oci8_ver = "2.1.x"
93
+ when "9.2"
94
+ ora_name = "Oracle 9iR2"
95
+ oci8_ver = "2.1.x"
96
+ end
97
+ raise "Ruby-oci8 #{OCI8::VERSION} doesn't support #{ora_name}. Use ruby-oci8 #{oci8_ver} instead."
98
+ end
86
99
  end
87
100
 
88
101
  $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
@@ -90,7 +103,7 @@ $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
90
103
  "stmt.o", "bind.o", "metadata.o", "attr.o",
91
104
  "lob.o", "oradate.o",
92
105
  "ocinumber.o", "ocidatetime.o", "object.o", "apiwrap.o",
93
- "encoding.o", "oranumber_util.o", "thread_util.o"]
106
+ "encoding.o", "oranumber_util.o", "thread_util.o", "util.o"]
94
107
 
95
108
  if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
96
109
  $defs << "-DUSE_WIN32_C"
@@ -104,24 +117,13 @@ end
104
117
 
105
118
  have_func("localtime_r")
106
119
  have_func("dladdr")
120
+ have_func("dlmodinfo")
121
+ have_func("dlgetname")
107
122
 
108
- # ruby 1.8 headers
109
- have_header("intern.h")
110
- have_header("util.h")
111
- # ruby 1.9.1 headers
112
- have_header("ruby/util.h")
113
- have_type('rb_encoding', ['ruby/ruby.h', 'ruby/encoding.h'])
114
123
  # ruby 2.0.0 headers
115
124
  have_header("ruby/thread.h")
116
125
 
117
- # $! in C API
118
- have_var("ruby_errinfo", "ruby.h") # ruby 1.8
119
- have_func("rb_errinfo", "ruby.h") # ruby 1.9
120
-
121
- have_func("rb_str_set_len", "ruby.h")
122
- have_func("rb_set_end_proc", "ruby.h")
123
126
  have_func("rb_class_superclass", "ruby.h")
124
- have_func("rb_thread_blocking_region", "ruby.h")
125
127
  have_func("rb_thread_call_without_gvl", "ruby/thread.h")
126
128
  have_func("rb_sym2str", "ruby.h")
127
129
  if (defined? RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
@@ -129,15 +131,6 @@ if (defined? RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
129
131
  have_func("rb_enc_str_buf_cat", "ruby.h")
130
132
  end
131
133
 
132
- # replace files
133
- replace = {
134
- 'OCI8_CLIENT_VERSION' => oraconf.version,
135
- 'OCI8_MODULE_VERSION' => RUBY_OCI8_VERSION
136
- }
137
-
138
- # make ruby script before running create_makefile.
139
- replace_keyword(File.dirname(__FILE__) + '/../../lib/oci8.rb.in', '../../lib/oci8.rb', replace)
140
-
141
134
  ruby_engine = (defined? RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
142
135
 
143
136
  so_basename = 'oci8lib_'
@@ -194,6 +187,7 @@ else
194
187
  plthook_src = "plthook_elf.c"
195
188
  end
196
189
  if xsystem(cc_command("").gsub(CONFTEST_C, File.dirname(__FILE__) + "/" + plthook_src))
190
+ File.delete(plthook_src.gsub(/\.c$/, '.' + RbConfig::CONFIG["OBJEXT"]))
197
191
  puts plthook_src
198
192
  $objs << plthook_src.gsub(/\.c$/, '.o')
199
193
  $objs << "hook_funcs.o"
@@ -204,7 +198,7 @@ end
204
198
 
205
199
  $defs << "-DInit_oci8lib=Init_#{so_basename}"
206
200
  $defs << "-Doci8lib=#{so_basename}"
207
- $defs << "-DOCI8LIB_VERSION=\\\"#{RUBY_OCI8_VERSION}\\\""
201
+ $defs << "-DOCI8LIB_VERSION=\\\"#{OCI8::VERSION}\\\""
208
202
  $defs << "-DCHAR_IS_NOT_A_SHORTCUT_TO_ID" if ruby_engine != 'ruby'
209
203
 
210
204
  create_header()
@@ -20,6 +20,10 @@ static VALUE seek_end;
20
20
 
21
21
  #define TO_LOB(obj) ((oci8_lob_t *)oci8_check_typeddata((obj), &oci8_lob_data_type, 1))
22
22
 
23
+ #ifndef MIN
24
+ #define MIN(a, b) ((a) < (b) ? (a) : (b))
25
+ #endif
26
+
23
27
  enum state {
24
28
  S_NO_OPEN_CLOSE,
25
29
  S_OPEN,
@@ -30,8 +34,7 @@ enum state {
30
34
  typedef struct {
31
35
  oci8_base_t base;
32
36
  oci8_svcctx_t *svcctx;
33
- ub4 pos;
34
- int char_width;
37
+ ub8 pos;
35
38
  ub1 csfrm;
36
39
  ub1 lobtype;
37
40
  enum state state;
@@ -65,8 +68,6 @@ static void oci8_lob_free(oci8_base_t *base)
65
68
  if (svcctx != NULL
66
69
  && OCILobIsTemporary(oci8_envhp, oci8_errhp, lob->base.hp.lob, &is_temporary) == OCI_SUCCESS
67
70
  && is_temporary) {
68
-
69
- #ifdef NATIVE_THREAD_WITH_GVL
70
71
  oci8_temp_lob_t *temp_lob = ALLOC(oci8_temp_lob_t);
71
72
 
72
73
  temp_lob->next = svcctx->temp_lobs;
@@ -75,10 +76,6 @@ static void oci8_lob_free(oci8_base_t *base)
75
76
  lob->base.type = 0;
76
77
  lob->base.closed = 1;
77
78
  lob->base.hp.ptr = NULL;
78
- #else
79
- /* FIXME: This may stall the GC. */
80
- OCILobFreeTemporary(svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob);
81
- #endif
82
79
  }
83
80
  lob->svcctx = NULL;
84
81
  }
@@ -259,12 +256,12 @@ void oci8_assign_bfile(oci8_svcctx_t *svcctx, VALUE lob, OCILobLocator **dest)
259
256
  oci8_assign_lob(cOCI8BFILE, svcctx, lob, dest);
260
257
  }
261
258
 
262
- static ub4 oci8_lob_get_length(oci8_lob_t *lob)
259
+ static ub8 oci8_lob_get_length(oci8_lob_t *lob)
263
260
  {
264
261
  oci8_svcctx_t *svcctx = check_svcctx(lob);
265
- ub4 len;
262
+ ub8 len;
266
263
 
267
- chker2(OCILobGetLength_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &len),
264
+ chker2(OCILobGetLength2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &len),
268
265
  &svcctx->base);
269
266
  return len;
270
267
  }
@@ -305,6 +302,10 @@ static void bfile_close(oci8_lob_t *lob)
305
302
  /*
306
303
  * Document-class: OCI8::LOB
307
304
  *
305
+ * OCI8::LOB is an I/O object. LOB contents are read from and written
306
+ * to the Oracle server via an associating connection. When a connection
307
+ * is closed, associating LOBs are also closed.
308
+ *
308
309
  * This is the abstract base class of large-object data types; {BFILE}, {BLOB}, {CLOB} and {NCLOB}.
309
310
  *
310
311
  */
@@ -387,7 +388,6 @@ static VALUE oci8_lob_do_initialize(int argc, VALUE *argv, VALUE self, ub1 csfrm
387
388
  oci8_env_raise(oci8_envhp, rv);
388
389
  lob->base.type = OCI_DTYPE_LOB;
389
390
  lob->pos = 0;
390
- lob->char_width = 1;
391
391
  lob->csfrm = csfrm;
392
392
  lob->lobtype = lobtype;
393
393
  lob->state = S_NO_OPEN_CLOSE;
@@ -473,26 +473,6 @@ static VALUE oci8_blob_initialize(int argc, VALUE *argv, VALUE self)
473
473
  return Qnil;
474
474
  }
475
475
 
476
- /*
477
- * call-seq:
478
- * __char_width = size
479
- *
480
- * @private
481
- * IMO, nobody need and use this.
482
- */
483
- static VALUE oci8_lob_set_char_width(VALUE self, VALUE vsize)
484
- {
485
- oci8_lob_t *lob = TO_LOB(self);
486
- int size;
487
-
488
- size = NUM2INT(vsize); /* 1 */
489
-
490
- if (size <= 0)
491
- rb_raise(rb_eArgError, "size must be more than one.");
492
- lob->char_width = size;
493
- return vsize;
494
- }
495
-
496
476
  /*
497
477
  * Returns +true+ when <i>self</i> is initialized.
498
478
  *
@@ -517,7 +497,7 @@ static VALUE oci8_lob_available_p(VALUE self)
517
497
  */
518
498
  static VALUE oci8_lob_get_size(VALUE self)
519
499
  {
520
- return UB4_TO_NUM(oci8_lob_get_length(TO_LOB(self)));
500
+ return ULL2NUM(oci8_lob_get_length(TO_LOB(self)));
521
501
  }
522
502
 
523
503
  /*
@@ -530,7 +510,7 @@ static VALUE oci8_lob_get_size(VALUE self)
530
510
  static VALUE oci8_lob_get_pos(VALUE self)
531
511
  {
532
512
  oci8_lob_t *lob = TO_LOB(self);
533
- return UB4_TO_NUM(lob->pos);
513
+ return ULL2NUM(lob->pos);
534
514
  }
535
515
 
536
516
  /*
@@ -577,11 +557,11 @@ static VALUE oci8_lob_seek(int argc, VALUE *argv, VALUE self)
577
557
  }
578
558
  }
579
559
  if (whence == seek_cur) {
580
- position = rb_funcall(UB4_TO_NUM(lob->pos), id_plus, 1, position);
560
+ position = rb_funcall(ULL2NUM(lob->pos), id_plus, 1, position);
581
561
  } else if (whence == seek_end) {
582
- position = rb_funcall(UB4_TO_NUM(oci8_lob_get_length(lob)), id_plus, 1, position);
562
+ position = rb_funcall(ULL2NUM(oci8_lob_get_length(lob)), id_plus, 1, position);
583
563
  }
584
- lob->pos = NUM2UINT(position);
564
+ lob->pos = NUM2ULL(position);
585
565
  return self;
586
566
  }
587
567
 
@@ -612,7 +592,7 @@ static VALUE oci8_lob_truncate(VALUE self, VALUE len)
612
592
  oci8_svcctx_t *svcctx = check_svcctx(lob);
613
593
 
614
594
  lob_open(lob);
615
- chker2(OCILobTrim_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, NUM2UINT(len)),
595
+ chker2(OCILobTrim2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, NUM2ULL(len)),
616
596
  &svcctx->base);
617
597
  return self;
618
598
  }
@@ -632,6 +612,32 @@ static VALUE oci8_lob_set_size(VALUE self, VALUE len)
632
612
  return len;
633
613
  }
634
614
 
615
+ static void open_bfile(oci8_svcctx_t *svcctx, oci8_lob_t *lob, OCIError *errhp)
616
+ {
617
+ while (1) {
618
+ sword rv = OCILobFileOpen_nb(svcctx, svcctx->base.hp.svc, errhp, lob->base.hp.lob, OCI_FILE_READONLY);
619
+ if (rv == OCI_ERROR && oci8_get_error_code(oci8_errhp) == 22290) {
620
+ /* ORA-22290: operation would exceed the maximum number of opened files or LOBs */
621
+ /* close all opened BFILE implicitly. */
622
+ oci8_base_t *base;
623
+ for (base = &lob->base; base != &lob->base; base = base->next) {
624
+ if (base->type == OCI_DTYPE_LOB) {
625
+ oci8_lob_t *tmp = (oci8_lob_t *)base;
626
+ if (tmp->state == S_BFILE_OPEN) {
627
+ chker2(OCILobFileClose_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, tmp->base.hp.lob),
628
+ &svcctx->base);
629
+ tmp->state = S_BFILE_CLOSE;
630
+ }
631
+ }
632
+ }
633
+ } else {
634
+ chker2(rv, &svcctx->base);
635
+ lob->state = S_BFILE_OPEN;
636
+ return;
637
+ }
638
+ }
639
+ }
640
+
635
641
  /*
636
642
  * @overload read
637
643
  *
@@ -659,61 +665,55 @@ static VALUE oci8_lob_read(int argc, VALUE *argv, VALUE self)
659
665
  {
660
666
  oci8_lob_t *lob = TO_LOB(self);
661
667
  oci8_svcctx_t *svcctx = check_svcctx(lob);
662
- ub4 length;
663
- ub4 nchar;
664
- long nbyte;
665
- ub4 amt;
668
+ ub8 lob_length;
669
+ ub8 read_len;
670
+ ub8 pos = lob->pos;
671
+ long strbufsiz;
672
+ ub8 byte_amt;
673
+ ub8 char_amt;
666
674
  sword rv;
667
- size_t buf_size_in_char;
668
675
  VALUE size;
669
676
  VALUE v = rb_ary_new();
677
+ OCIError *errhp = oci8_errhp;
678
+ ub1 piece = OCI_FIRST_PIECE;
670
679
 
671
680
  rb_scan_args(argc, argv, "01", &size);
672
- length = oci8_lob_get_length(lob);
673
- if (length == 0 && NIL_P(size)) {
681
+ lob_length = oci8_lob_get_length(lob);
682
+ if (lob_length == 0 && NIL_P(size)) {
674
683
  return rb_usascii_str_new("", 0);
675
684
  }
676
- if (length <= lob->pos) /* EOF */
685
+ if (lob_length <= pos) /* EOF */
677
686
  return Qnil;
678
- length -= lob->pos;
679
687
  if (NIL_P(size)) {
680
- nchar = length; /* read until EOF */
688
+ read_len = lob_length - pos;
681
689
  } else {
682
- nchar = NUM2UINT(size);
683
- if (nchar > length)
684
- nchar = length;
690
+ ub8 sz = NUM2ULL(size);
691
+ read_len = MIN(sz, lob_length - pos);
692
+ }
693
+ if (lob->lobtype == OCI_TEMP_CLOB) {
694
+ byte_amt = 0;
695
+ char_amt = read_len;
696
+ if (oci8_nls_ratio == 1) {
697
+ strbufsiz = MIN(read_len, ULONG_MAX);
698
+ } else {
699
+ strbufsiz = MIN(read_len + read_len / 8, ULONG_MAX);
700
+ }
701
+ if (strbufsiz <= 10) {
702
+ strbufsiz = 10;
703
+ }
704
+ } else {
705
+ byte_amt = read_len;
706
+ char_amt = 0;
707
+ strbufsiz = MIN(read_len, ULONG_MAX);
708
+ }
709
+ if (lob->state == S_BFILE_CLOSE) {
710
+ open_bfile(svcctx, lob, errhp);
685
711
  }
686
- nbyte = oci8_nls_ratio * (long)nchar;
687
- amt = nchar;
688
- buf_size_in_char = nbyte / lob->char_width;
689
712
  do {
690
- VALUE strbuf = rb_str_buf_new(nbyte);
713
+ VALUE strbuf = rb_str_buf_new(strbufsiz);
691
714
  char *buf = RSTRING_PTR(strbuf);
692
715
 
693
- if (lob->state == S_BFILE_CLOSE) {
694
- rv = OCILobFileOpen_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, OCI_FILE_READONLY);
695
- if (rv == OCI_ERROR && oci8_get_error_code(oci8_errhp) == 22290) {
696
- /* ORA-22290: operation would exceed the maximum number of opened files or LOBs */
697
- /* close all opened BFILE implicitly. */
698
- oci8_base_t *base;
699
- for (base = &lob->base; base != &lob->base; base = base->next) {
700
- if (base->type == OCI_DTYPE_LOB) {
701
- oci8_lob_t *tmp = (oci8_lob_t *)base;
702
- if (tmp->state == S_BFILE_OPEN) {
703
- tmp->state = S_BFILE_CLOSE;
704
- }
705
- }
706
- }
707
- chker2(OCILobFileCloseAll_nb(svcctx, svcctx->base.hp.svc, oci8_errhp),
708
- &svcctx->base);
709
- continue;
710
- }
711
- chker2(rv, &svcctx->base);
712
- lob->state = S_BFILE_OPEN;
713
- }
714
- /* initialize buf in zeros everytime to check a nul characters. */
715
- memset(buf, 0, nbyte);
716
- rv = OCILobRead_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &amt, lob->pos + 1, buf, nbyte, NULL, NULL, 0, lob->csfrm);
716
+ rv = OCILobRead2_nb(svcctx, svcctx->base.hp.svc, errhp, lob->base.hp.lob, &byte_amt, &char_amt, pos + 1, buf, strbufsiz, piece, NULL, NULL, 0, lob->csfrm);
717
717
  svcctx->suppress_free_temp_lobs = 0;
718
718
  switch (rv) {
719
719
  case OCI_SUCCESS:
@@ -723,48 +723,32 @@ static VALUE oci8_lob_read(int argc, VALUE *argv, VALUE self)
723
723
  * See: https://github.com/kubo/ruby-oci8/issues/20
724
724
  */
725
725
  svcctx->suppress_free_temp_lobs = 1;
726
+ piece = OCI_NEXT_PIECE;
726
727
  break;
727
- case OCI_ERROR:
728
- if (oci8_get_error_code(oci8_errhp) == 22289) {
729
- /* ORA-22289: cannot perform FILEREAD operation on an unopened file or LOB */
730
- if (lob->state == S_BFILE_CLOSE)
731
- continue;
732
- }
733
- /* FALLTHROUGH */
734
728
  default:
735
729
  chker2(rv, &svcctx->base);
736
730
  }
737
-
738
- /* Workaround when using Oracle 10.2.0.4 or 11.1.0.6 client and
739
- * variable-length character set (e.g. AL32UTF8).
740
- *
741
- * When the above mentioned condition, amt may be shorter. So
742
- * amt is increaded until a nul character to know the actually
743
- * read size.
744
- */
745
- while (amt < nbyte && buf[amt] != '\0') {
746
- amt++;
747
- }
748
-
749
- if (amt == 0)
731
+ if (byte_amt == 0)
750
732
  break;
751
- /* for fixed size charset, amt is the number of characters stored in buf. */
752
- if (amt > buf_size_in_char)
753
- rb_raise(eOCIException, "Too large buffer fetched or you set too large size of a character.");
754
- amt *= lob->char_width;
755
- rb_str_set_len(strbuf, amt);
733
+ if (lob->lobtype == OCI_TEMP_CLOB) {
734
+ pos += char_amt;
735
+ } else {
736
+ pos += byte_amt;
737
+ }
738
+ rb_str_set_len(strbuf, byte_amt);
756
739
  rb_ary_push(v, strbuf);
757
740
  } while (rv == OCI_NEED_DATA);
758
- lob->pos += nchar;
759
- if (nchar == length) {
741
+
742
+ if (pos >= lob_length) {
760
743
  lob_close(lob);
761
744
  bfile_close(lob);
762
745
  }
746
+ lob->pos = pos;
763
747
  switch (RARRAY_LEN(v)) {
764
748
  case 0:
765
749
  return Qnil;
766
750
  case 1:
767
- v = RARRAY_PTR(v)[0];
751
+ v = RARRAY_AREF(v, 0);
768
752
  break;
769
753
  default:
770
754
  v = rb_ary_join(v, Qnil);
@@ -793,25 +777,35 @@ static VALUE oci8_lob_write(VALUE self, VALUE data)
793
777
  {
794
778
  oci8_lob_t *lob = TO_LOB(self);
795
779
  oci8_svcctx_t *svcctx = check_svcctx(lob);
796
- ub4 amt;
780
+ volatile VALUE str;
781
+ ub8 byte_amt;
782
+ ub8 char_amt;
797
783
 
798
784
  lob_open(lob);
799
785
  if (TYPE(data) != T_STRING) {
800
- data = rb_obj_as_string(data);
786
+ str = rb_obj_as_string(data);
787
+ } else {
788
+ str = data;
801
789
  }
802
790
  if (lob->lobtype == OCI_TEMP_CLOB) {
803
- data = rb_str_export_to_enc(data, oci8_encoding);
791
+ str = rb_str_export_to_enc(str, oci8_encoding);
804
792
  }
805
- RB_GC_GUARD(data);
806
- amt = RSTRING_LEN(data);
807
- if (amt == 0) {
793
+ byte_amt = RSTRING_LEN(str);
794
+ if (byte_amt == 0) {
808
795
  /* to avoid ORA-24801: illegal parameter value in OCI lob function */
809
796
  return INT2FIX(0);
810
797
  }
811
- chker2(OCILobWrite_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &amt, lob->pos + 1, RSTRING_PTR(data), amt, OCI_ONE_PIECE, NULL, NULL, 0, lob->csfrm),
798
+ char_amt = 0;
799
+ chker2(OCILobWrite2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &byte_amt, &char_amt, lob->pos + 1, RSTRING_PTR(str), byte_amt, OCI_ONE_PIECE, NULL, NULL, 0, lob->csfrm),
812
800
  &svcctx->base);
813
- lob->pos += amt;
814
- return UINT2NUM(amt);
801
+ RB_GC_GUARD(str);
802
+ if (lob->lobtype == OCI_TEMP_CLOB) {
803
+ lob->pos += char_amt;
804
+ return UINT2NUM(char_amt);
805
+ } else {
806
+ lob->pos += byte_amt;
807
+ return UINT2NUM(byte_amt);
808
+ }
815
809
  }
816
810
 
817
811
  /*
@@ -972,7 +966,6 @@ static VALUE oci8_bfile_initialize(int argc, VALUE *argv, VALUE self)
972
966
  }
973
967
  lob->base.type = OCI_DTYPE_LOB;
974
968
  lob->pos = 0;
975
- lob->char_width = 1;
976
969
  lob->csfrm = SQLCS_IMPLICIT;
977
970
  lob->lobtype = OCI_TEMP_BLOB;
978
971
  lob->state = S_BFILE_CLOSE;
@@ -1294,7 +1287,6 @@ void Init_oci8_lob(VALUE cOCI8)
1294
1287
  rb_define_method(cOCI8CLOB, "initialize", oci8_clob_initialize, -1);
1295
1288
  rb_define_method(cOCI8NCLOB, "initialize", oci8_nclob_initialize, -1);
1296
1289
  rb_define_method(cOCI8BLOB, "initialize", oci8_blob_initialize, -1);
1297
- rb_define_private_method(cOCI8LOB, "__char_width=", oci8_lob_set_char_width, 1);
1298
1290
  rb_define_method(cOCI8LOB, "available?", oci8_lob_available_p, 0);
1299
1291
  rb_define_method(cOCI8LOB, "size", oci8_lob_get_size, 0);
1300
1292
  rb_define_method(cOCI8LOB, "pos", oci8_lob_get_pos, 0);