/* * @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); } }
static ub8 oci8_lob_get_length(oci8_lob_t *lob) { oci8_svcctx_t *svcctx = check_svcctx(lob); ub8 len; chker2(OCILobGetLength2_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, &len), &svcctx->base); return len; }
/* * 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); }
static void bfile_close(oci8_lob_t *lob) { if (lob->state == S_BFILE_OPEN) { oci8_svcctx_t *svcctx = check_svcctx(lob); chker2(OCILobFileClose_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob), &svcctx->base); lob->state = S_BFILE_CLOSE; } }
static void lob_open(oci8_lob_t *lob) { if (lob->state == S_CLOSE) { oci8_svcctx_t *svcctx = check_svcctx(lob); chker2(OCILobOpen_nb(svcctx, svcctx->base.hp.svc, oci8_errhp, lob->base.hp.lob, OCI_DEFAULT), &svcctx->base); lob->state = S_OPEN; } }
/* * @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; }
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; }
/* * @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; } }