ruby-oci8 2.1.8 → 2.2.0.1

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