static VALUE oci8_lob_do_initialize(int argc, VALUE *argv, VALUE self, ub1 csfrm, ub1 lobtype) { oci8_lob_t *lob = TO_LOB(self); VALUE svc; VALUE val; oci8_svcctx_t *svcctx; sword rv; rb_scan_args(argc, argv, "11", &svc, &val); svcctx = oci8_get_svcctx(svc); rv = OCIDescriptorAlloc(oci8_envhp, &lob->base.hp.ptr, OCI_DTYPE_LOB, 0, NULL); if (rv != OCI_SUCCESS) oci8_env_raise(oci8_envhp, rv); lob->base.type = OCI_DTYPE_LOB; lob->pos = 0; lob->csfrm = csfrm; lob->lobtype = lobtype; lob->state = S_NO_OPEN_CLOSE; oci8_link_to_parent(&lob->base, &svcctx->base); lob->svcctx = svcctx; RB_OBJ_WRITTEN(self, Qundef, svc); if (!NIL_P(val)) { OCI8StringValue(val); chker2(OCILobCreateTemporary_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, 0, csfrm, lobtype, TRUE, OCI_DURATION_SESSION), &svcctx->base); oci8_lob_write(self, val); lob->pos = 0; /* reset the position */ } return Qnil; }
/* * Closes the lob. * * @return [self] */ static VALUE oci8_lob_close(VALUE self) { oci8_lob_t *lob = TO_LOB(self); lob_close(lob); oci8_base_free(&lob->base); return self; }
/* * @overload write(data) * * Writes +data+ to LOB. * * @param [String] data * @return [Integer] number of characters written if +self+ is a {CLOB} or a {NCLOB}. * number of bytes written if +self+ is a {BLOB} or a {BFILE}. */ static VALUE oci8_lob_write(VALUE self, VALUE data) { oci8_lob_t *lob = TO_LOB(self); oci8_svcctx_t *svcctx = check_svcctx(lob); volatile VALUE str; ub8 byte_amt; ub8 char_amt; lob_open(lob); if (TYPE(data) != T_STRING) { str = rb_obj_as_string(data); } else { str = data; } if (lob->lobtype == OCI_TEMP_CLOB) { str = rb_str_export_to_enc(str, oci8_encoding); } byte_amt = RSTRING_LEN(str); if (byte_amt == 0) { /* to avoid ORA-24801: illegal parameter value in OCI lob function */ return INT2FIX(0); } char_amt = 0; 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), &svcctx->base); RB_GC_GUARD(str); if (lob->lobtype == OCI_TEMP_CLOB) { lob->pos += char_amt; return UINT2NUM(char_amt); } else { lob->pos += byte_amt; return UINT2NUM(byte_amt); } }
/* * @overload initialize(conn, directory = nil, filename = nil) * * Creates a BFILE object. * This is correspond to {BFILENAME}[https://docs.oracle.com/database/121/SQLRF/functions020.htm]. * * @param [OCI8] conn * @param [String] directory a directory object created by * {"CREATE DIRECTORY"}[http://docs.oracle.com/database/121/SQLRF/statements_5008.htm]. * @param [String] filename * @return [OCI8::BFILE] */ static VALUE oci8_bfile_initialize(int argc, VALUE *argv, VALUE self) { oci8_lob_t *lob = TO_LOB(self); VALUE svc; VALUE dir_alias; VALUE filename; oci8_svcctx_t *svcctx; int rv; rb_scan_args(argc, argv, "12", &svc, &dir_alias, &filename); svcctx = oci8_get_svcctx(svc); rv = OCIDescriptorAlloc(oci8_envhp, &lob->base.hp.ptr, OCI_DTYPE_LOB, 0, NULL); if (rv != OCI_SUCCESS) { oci8_env_raise(oci8_envhp, rv); } lob->base.type = OCI_DTYPE_LOB; lob->pos = 0; lob->csfrm = SQLCS_IMPLICIT; lob->lobtype = OCI_TEMP_BLOB; lob->state = S_BFILE_CLOSE; if (argc != 1) { OCI8SafeStringValue(dir_alias); OCI8SafeStringValue(filename); oci8_bfile_set_name(self, dir_alias, filename); } oci8_link_to_parent(&lob->base, &svcctx->base); lob->svcctx = svcctx; return Qnil; }
/* * Returns true if the current offset is at end of lob. * * @return [true or false] */ static VALUE oci8_lob_eof_p(VALUE self) { oci8_lob_t *lob = TO_LOB(self); if (oci8_lob_get_length(lob) < lob->pos) return Qfalse; else return Qtrue; }
/* * Returns +true+ when <i>self</i> is initialized. * * @return [true or false] */ static VALUE oci8_lob_available_p(VALUE self) { oci8_lob_t *lob = TO_LOB(self); boolean is_initialized; chker2(OCILobLocatorIsInit(oci8_envhp, oci8_errhp, lob->base.hp.lob, &is_initialized), &lob->base); return is_initialized ? Qtrue : Qfalse; }
/* * call-seq: * truncate(length) * * @param [Integer] length length in characters if +self+ is a {CLOB} or a {NCLOB}. * length in bytes if +self+ is a {BLOB} or a {BFILE}. * @return [self] * @see #size= */ static VALUE oci8_lob_truncate(VALUE self, VALUE len) { oci8_lob_t *lob = TO_LOB(self); oci8_svcctx_t *svcctx = check_svcctx(lob); chker2(OCILobTrim2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, NUM2ULL(len)), &svcctx->base); return self; }
/* * Returns the chunk size of a LOB. * * @see http://docs.oracle.com/database/121/ARPLS/d_lob.htm#ARPLS66706 DBMS_LOB.GETCHUNKSIZE * @return [Integer] */ static VALUE oci8_lob_get_chunk_size(VALUE self) { oci8_lob_t *lob = TO_LOB(self); oci8_svcctx_t *svcctx = check_svcctx(lob); ub4 len; chker2(OCILobGetChunkSize_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &len), &svcctx->base); return UINT2NUM(len); }
/* * @overload exists? * * Returns <code>true</code> when the BFILE exists on the server's operating system. */ static VALUE oci8_bfile_exists_p(VALUE self) { oci8_lob_t *lob = TO_LOB(self); oci8_svcctx_t *svcctx = check_svcctx(lob); boolean flag; chker2(OCILobFileExists_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &flag), &svcctx->base); return flag ? Qtrue : Qfalse; }
/* * @deprecated I'm not sure that this is what the name indicates. * @private */ static VALUE oci8_lob_set_sync(VALUE self, VALUE b) { oci8_lob_t *lob = TO_LOB(self); if (RTEST(b)) { lob_close(lob); lob->state = S_NO_OPEN_CLOSE; } else { if (lob->state == S_NO_OPEN_CLOSE) lob->state = S_CLOSE; } return b; }
static void oci8_bfile_set_name(VALUE self, VALUE dir_alias, VALUE filename) { oci8_lob_t *lob = TO_LOB(self); bfile_close(lob); if (RSTRING_LEN(dir_alias) > UB2MAXVAL) { rb_raise(rb_eRuntimeError, "dir_alias is too long."); } if (RSTRING_LEN(filename) > UB2MAXVAL) { rb_raise(rb_eRuntimeError, "filename is too long."); } chker2(OCILobFileSetName(oci8_envhp, oci8_errhp, &lob->base.hp.lob, RSTRING_ORATEXT(dir_alias), (ub2)RSTRING_LEN(dir_alias), RSTRING_ORATEXT(filename), (ub2)RSTRING_LEN(filename)), &lob->base); }
static VALUE oci8_make_lob(VALUE klass, oci8_svcctx_t *svcctx, OCILobLocator *s) { oci8_lob_t *lob; boolean is_temp; VALUE lob_obj; lob_obj = rb_class_new_instance(1, &svcctx->base.self, klass); lob = TO_LOB(lob_obj); /* If 's' is a temporary lob, use OCILobLocatorAssign instead. */ chker2(OCILobIsTemporary(oci8_envhp, oci8_errhp, s, &is_temp), &svcctx->base); if (is_temp) chker2(OCILobLocatorAssign_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, s, &lob->base.hp.lob), &svcctx->base); else chker2(OCILobAssign(oci8_envhp, oci8_errhp, s, &lob->base.hp.lob), &svcctx->base); return lob_obj; }
static VALUE oci8_lob_clone(VALUE self) { oci8_lob_t *lob = TO_LOB(self); oci8_lob_t *newlob; VALUE newobj = lob->svcctx ? lob->svcctx->base.self : Qnil; boolean is_temporary; newobj = rb_class_new_instance(1, &newobj, CLASS_OF(self)); newlob = DATA_PTR(newobj); if (OCILobIsTemporary(oci8_envhp, oci8_errhp, lob->base.hp.lob, &is_temporary) == OCI_SUCCESS && is_temporary) { oci8_svcctx_t *svcctx = check_svcctx(lob); chker2(OCILobLocatorAssign_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &newlob->base.hp.lob), &svcctx->base); } else { chker2(OCILobAssign(oci8_envhp, oci8_errhp, lob->base.hp.lob, &newlob->base.hp.lob), &lob->base); } return newobj; }
/* * call-seq: * seek(amount, whence=IO::SEEK_SET) * * Seeks to the given offset in the stream. The new position, measured in characters, * is obtained by adding offset <i>amount</i> to the position specified by <i>whence</i>. * If <i>whence</i> is set to IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END, * the offset is relative to the start of the file, the current position * indicator, or end-of-file, respectively. * * @param [Integer] amount * @param [IO::SEEK_SET, IO::SEEK_CUR or IO::SEEK_END] whence * @return [self] */ static VALUE oci8_lob_seek(int argc, VALUE *argv, VALUE self) { oci8_lob_t *lob = TO_LOB(self); VALUE position, whence; rb_scan_args(argc, argv, "11", &position, &whence); if (argc == 2 && (whence != seek_set && whence != seek_cur && whence != seek_end)) { if (FIXNUM_P(whence)) { rb_raise(rb_eArgError, "expect IO::SEEK_SET, IO::SEEK_CUR or IO::SEEK_END but %d", FIX2INT(whence)); } else { rb_raise(rb_eArgError, "expect IO::SEEK_SET, IO::SEEK_CUR or IO::SEEK_END but %s", rb_class2name(CLASS_OF(whence))); } } if (whence == seek_cur) { position = rb_funcall(ULL2NUM(lob->pos), id_plus, 1, position); } else if (whence == seek_end) { position = rb_funcall(ULL2NUM(oci8_lob_get_length(lob)), id_plus, 1, position); } lob->pos = NUM2ULL(position); return self; }
static void oci8_bfile_get_name(VALUE self, VALUE *dir_alias_p, VALUE *filename_p) { int need_get = 0; if (dir_alias_p != NULL) { *dir_alias_p = rb_ivar_get(self, id_dir_alias); if (NIL_P(*dir_alias_p)) need_get = 1; } if (filename_p != NULL) { *filename_p = rb_ivar_get(self, id_filename); if (NIL_P(*filename_p)) need_get = 1; } if (need_get) { oci8_lob_t *lob = TO_LOB(self); char d_buf[31]; ub2 d_length = sizeof(d_buf); char f_buf[256]; ub2 f_length = sizeof(f_buf); VALUE dir_alias; VALUE filename; chker2(OCILobFileGetName(oci8_envhp, oci8_errhp, lob->base.hp.lob, TO_ORATEXT(d_buf), &d_length, TO_ORATEXT(f_buf), &f_length), &lob->base); dir_alias = rb_external_str_new_with_enc(d_buf, d_length, oci8_encoding); filename = rb_external_str_new_with_enc(f_buf, f_length, oci8_encoding); rb_ivar_set(self, id_dir_alias, dir_alias); rb_ivar_set(self, id_filename, filename); if (dir_alias_p != NULL) { *dir_alias_p = dir_alias; } if (filename_p != NULL) { *filename_p = filename; } } }
/* * Sets the current offset at the beginning. * * @return [true or false] */ static VALUE oci8_lob_rewind(VALUE self) { oci8_lob_t *lob = TO_LOB(self); lob->pos = 0; return self; }
/* * @deprecated I'm not sure that this is what the name indicates. * @private */ static VALUE oci8_lob_flush(VALUE self) { oci8_lob_t *lob = TO_LOB(self); lob_close(lob); return self; }
/* * @deprecated I'm not sure that this is what the name indicates. * @private */ static VALUE oci8_lob_get_sync(VALUE self) { oci8_lob_t *lob = TO_LOB(self); return (lob->state == S_NO_OPEN_CLOSE) ? Qtrue : Qfalse; }
/* * @overload read * * * * @param [Integer] length number of characters if +self+ is a {CLOB} or a {NCLOB}. * number of bytes if +self+ is a {BLOB} or a {BFILE}. * @return [String or nil] data read. <code>nil</code> means it * met EOF at beginning. It returns an empty string '' as a special exception * when <i>length</i> is <code>nil</code> and the lob is empty. * * @overload read(length) * * Reads <i>length</i> characters for {CLOB} and {NCLOB} or <i>length</i> * bytes for {BLOB} and {BFILE} from the current position. * If <i>length</i> is <code>nil</code>, it reads data until EOF. * * @param [Integer] length number of characters if +self+ is a {CLOB} or a {NCLOB}. * number of bytes if +self+ is a {BLOB} or a {BFILE}. * @return [String or nil] data read. <code>nil</code> means it * met EOF at beginning. It returns an empty string '' as a special exception * when <i>length</i> is <code>nil</code> and the lob is empty. */ static VALUE oci8_lob_read(int argc, VALUE *argv, VALUE self) { oci8_lob_t *lob = TO_LOB(self); oci8_svcctx_t *svcctx = check_svcctx(lob); ub8 lob_length; ub8 read_len; ub8 pos = lob->pos; long strbufsiz; ub8 byte_amt; ub8 char_amt; sword rv; VALUE size; VALUE v = rb_ary_new(); OCIError *errhp = oci8_errhp; ub1 piece = OCI_FIRST_PIECE; rb_scan_args(argc, argv, "01", &size); lob_length = oci8_lob_get_length(lob); if (lob_length == 0 && NIL_P(size)) { return rb_usascii_str_new("", 0); } if (lob_length <= pos) /* EOF */ return Qnil; if (NIL_P(size)) { read_len = lob_length - pos; } else { ub8 sz = NUM2ULL(size); read_len = MIN(sz, lob_length - pos); } if (lob->lobtype == OCI_TEMP_CLOB) { byte_amt = 0; char_amt = read_len; if (oci8_nls_ratio == 1) { strbufsiz = MIN(read_len, ULONG_MAX); } else { strbufsiz = MIN(read_len + read_len / 8, ULONG_MAX); } if (strbufsiz <= 10) { strbufsiz = 10; } } else { byte_amt = read_len; char_amt = 0; strbufsiz = MIN(read_len, ULONG_MAX); } if (lob->state == S_BFILE_CLOSE) { open_bfile(svcctx, lob, errhp); } do { VALUE strbuf = rb_str_buf_new(strbufsiz); char *buf = RSTRING_PTR(strbuf); 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); svcctx->suppress_free_temp_lobs = 0; switch (rv) { case OCI_SUCCESS: break; case OCI_NEED_DATA: /* prevent OCILobFreeTemporary() from being called. * See: https://github.com/kubo/ruby-oci8/issues/20 */ svcctx->suppress_free_temp_lobs = 1; piece = OCI_NEXT_PIECE; break; default: chker2(rv, &svcctx->base); } if (byte_amt == 0) break; if (lob->lobtype == OCI_TEMP_CLOB) { pos += char_amt; } else { pos += byte_amt; } rb_str_set_len(strbuf, byte_amt); rb_ary_push(v, strbuf); } while (rv == OCI_NEED_DATA); if (pos >= lob_length) { lob_close(lob); bfile_close(lob); } lob->pos = pos; switch (RARRAY_LEN(v)) { case 0: return Qnil; case 1: v = RARRAY_AREF(v, 0); break; default: v = rb_ary_join(v, Qnil); } OBJ_TAINT(v); if (lob->lobtype == OCI_TEMP_CLOB) { /* set encoding */ rb_enc_associate(v, oci8_encoding); return rb_str_conv_enc(v, oci8_encoding, rb_default_internal_encoding()); } else { /* ASCII-8BIT */ return v; } }
/* * Returns the size. * For CLOB and NCLOB it is the number of characters, * for BLOB and BFILE it is the number of bytes. * * @return [Integer] */ static VALUE oci8_lob_get_size(VALUE self) { return ULL2NUM(oci8_lob_get_length(TO_LOB(self))); }
/* * Returns the current offset. * For CLOB and NCLOB it is the number of characters, * for BLOB and BFILE it is the number of bytes. * * @return [Integer] */ static VALUE oci8_lob_get_pos(VALUE self) { oci8_lob_t *lob = TO_LOB(self); return ULL2NUM(lob->pos); }