DBD-Oracle
view release on metacpan or search on metacpan
#ifdef OCI_ATTR_RESERVED_15
case OCI_ATTR_RESERVED_15: return "OCI_ATTR_RESERVED_15"; /* reserved */
#endif
#ifdef OCI_ATTR_RESERVED_16
case OCI_ATTR_RESERVED_16: return "OCI_ATTR_RESERVED_16"; /* reserved */
#endif
}
sv = sv_2mortal(newSViv((IV)attr));
return SvPV(sv,PL_na);
}
/*used to look up the name of a fetchtype constant
used only for debugging */
char *
oci_fetch_options(ub4 fetchtype)
{
dTHX;
SV *sv;
switch (fetchtype) {
/* fetch options */
case OCI_FETCH_CURRENT: return "OCI_FETCH_CURRENT";
case OCI_FETCH_NEXT: return "OCI_FETCH_NEXT";
case OCI_FETCH_FIRST: return "OCI_FETCH_FIRST";
case OCI_FETCH_LAST: return "OCI_FETCH_LAST";
case OCI_FETCH_PRIOR: return "OCI_FETCH_PRIOR";
case OCI_FETCH_ABSOLUTE: return "OCI_FETCH_ABSOLUTE";
case OCI_FETCH_RELATIVE: return "OCI_FETCH_RELATIVE";
}
sv = sv_2mortal(newSViv((IV)fetchtype));
return SvPV(sv,PL_na);
}
static sb4
oci_error_get(imp_xxh_t *imp_xxh,
OCIError *errhp, sword status, char *what, SV *errstr, int debug)
{
dTHX;
text errbuf[1024];
ub4 recno = 0;
sb4 errcode = 0;
sb4 eg_errcode = 0;
sword eg_status;
if (!SvOK(errstr))
sv_setpv(errstr,"");
if (!errhp) {
sv_catpv(errstr, oci_status_name(status));
if (what) {
sv_catpv(errstr, " ");
sv_catpv(errstr, what);
}
return status;
}
while( ++recno
&& OCIErrorGet_log_stat(imp_xxh, errhp, recno, (text*)NULL, &eg_errcode, errbuf,
(ub4)sizeof(errbuf), OCI_HTYPE_ERROR, eg_status) != OCI_NO_DATA
&& eg_status != OCI_INVALID_HANDLE
&& recno < 100) {
if (debug >= 4 || recno>1/*XXX temp*/)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
" OCIErrorGet after %s (er%ld:%s): %d, %ld: %s\n",
what ? what : "<NULL>", (long)recno,
(eg_status==OCI_SUCCESS) ? "ok" : oci_status_name(eg_status),
status, (long)eg_errcode, errbuf);
errcode = eg_errcode;
sv_catpv(errstr, (char*)errbuf);
if (*(SvEND(errstr)-1) == '\n')
--SvCUR(errstr);
}
if (what || status != OCI_ERROR) {
sv_catpv(errstr, (debug<0) ? " (" : " (DBD ");
sv_catpv(errstr, oci_status_name(status));
if (what) {
sv_catpv(errstr, ": ");
sv_catpv(errstr, what);
}
sv_catpv(errstr, ")");
}
return errcode;
}
int
oci_error_err(SV *h, OCIError *errhp, sword status, char *what, sb4 force_err)
{
dTHX;
D_imp_xxh(h);
sb4 errcode;
SV *errstr_sv = sv_newmortal();
SV *errcode_sv = sv_newmortal();
errcode = oci_error_get(imp_xxh, errhp, status, what, errstr_sv,
DBIc_DBISTATE(imp_xxh)->debug);
if (CSFORM_IMPLIES_UTF8(SQLCS_IMPLICIT)) {
#ifdef sv_utf8_decode
sv_utf8_decode(errstr_sv);
#else
SvUTF8_on(errstr_sv);
#endif
}
/* DBIc_ERR *must* be SvTRUE (for RaiseError etc), some */
/* errors, like OCI_INVALID_HANDLE, don't set errcode. */
if (force_err)
errcode = force_err;
if (status == OCI_SUCCESS_WITH_INFO)
errcode = 0; /* record as a "warning" for DBI>=1.43 */
else if (errcode == 0)
errcode = (status != 0) ? status : -10000;
sv_setiv(errcode_sv, errcode);
DBIh_SET_ERR_SV(h, imp_xxh, errcode_sv, errstr_sv, &PL_sv_undef, &PL_sv_undef);
return 0; /* always returns 0 */
}
char *
ora_sql_error(imp_sth_t *imp_sth, char *msg)
{
dTHX;
#ifdef OCI_ATTR_PARSE_ERROR_OFFSET
D_imp_dbh_from_sth;
SV *msgsv, *sqlsv;
char buf[99];
sword status = 0;
ub2 parse_error_offset = 0;
OCIAttrGet_stmhp_stat(imp_sth, &parse_error_offset, 0,
OCI_ATTR_PARSE_ERROR_OFFSET, status);
imp_dbh->parse_error_offset = parse_error_offset;
if (!parse_error_offset)
return msg;
sprintf(buf,"error possibly near <*> indicator at char %d in '",
parse_error_offset);
msgsv = sv_2mortal(newSVpv(buf,0));
sqlsv = sv_2mortal(newSVpv(imp_sth->statement,0));
sv_insert(sqlsv, parse_error_offset, 0, "<*>", 3);
sv_catsv(msgsv, sqlsv);
sv_catpv(msgsv, "'");
return SvPV(msgsv,PL_na);
#else
imp_sth = imp_sth; /* not unused */
return msg;
#endif
}
void *
oci_db_handle(imp_dbh_t *imp_dbh, int handle_type, int flags)
{
dTHX;
switch(handle_type) {
case OCI_HTYPE_ENV: return imp_dbh->envhp;
case OCI_HTYPE_ERROR: return imp_dbh->errhp;
case OCI_HTYPE_SERVER: return imp_dbh->srvhp;
case OCI_HTYPE_SVCCTX: return imp_dbh->svchp;
case OCI_HTYPE_SESSION: return imp_dbh->seshp;
/*case OCI_HTYPE_AUTHINFO:return imp_dbh->authp;*/
}
croak("Can't get OCI handle type %d from DBI database handle", handle_type);
if( flags ) {/* For GCC not to warn on unused parameter */}
/* satisfy compiler warning, even though croak will never return */
return 0;
}
void *
oci_st_handle(imp_sth_t *imp_sth, int handle_type, int flags)
{
dTHX;
switch(handle_type) {
case OCI_HTYPE_ENV: return imp_sth->envhp;
case OCI_HTYPE_ERROR: return imp_sth->errhp;
case OCI_HTYPE_SERVER: return imp_sth->srvhp;
case OCI_HTYPE_SVCCTX: return imp_sth->svchp;
case OCI_HTYPE_STMT: return imp_sth->stmhp;
}
croak("Can't get OCI handle type %d from DBI statement handle", handle_type);
if( flags ) {/* For GCC not to warn on unused parameter */}
/* satisfy compiler warning, even though croak will never return */
return 0;
}
int
dbd_st_prepare(SV *sth, imp_sth_t *imp_sth, char *statement, SV *attribs)
{
dTHX;
D_imp_dbh_from_sth;
if (DBIc_COMPAT(imp_sth)) {
static SV *ora_pad_empty;
if (!ora_pad_empty) {
ora_pad_empty= perl_get_sv("Oraperl::ora_pad_empty", GV_ADDMULTI);
if (!SvOK(ora_pad_empty) && getenv("ORAPERL_PAD_EMPTY"))
sv_setiv(ora_pad_empty, atoi(getenv("ORAPERL_PAD_EMPTY")));
}
imp_sth->ora_pad_empty = (SvOK(ora_pad_empty)) ? SvIV(ora_pad_empty) : 0;
}
imp_sth->auto_lob = 1;
imp_sth->exe_mode = OCI_DEFAULT;
if (attribs) {
SV **svp;
IV ora_auto_lob = 1;
DBD_ATTRIB_GET_IV( attribs, "ora_placeholders", 16, svp, ora_placeholders);
DBD_ATTRIB_GET_IV( attribs, "ora_auto_lob", 12, svp, ora_auto_lob);
DBD_ATTRIB_GET_IV( attribs, "ora_pers_lob", 12, svp, ora_pers_lob);
DBD_ATTRIB_GET_IV( attribs, "ora_clbk_lob", 12, svp, ora_clbk_lob);
DBD_ATTRIB_GET_IV( attribs, "ora_piece_lob", 13, svp, ora_piece_lob);
DBD_ATTRIB_GET_IV( attribs, "ora_piece_size", 14, svp, ora_piece_size);
imp_sth->auto_lob = (ora_auto_lob) ? 1 : 0;
imp_sth->pers_lob = (ora_pers_lob) ? 1 : 0;
imp_sth->clbk_lob = (ora_clbk_lob) ? 1 : 0;
imp_sth->piece_lob = (ora_piece_lob) ? 1 : 0;
imp_sth->piece_size = (ora_piece_size) ? ora_piece_size : 0;
imp_sth->prefetch_rows = 0;
imp_sth->prefetch_memory= 0;
/* ora_check_sql only works for selects owing to Oracle behaviour */
DBD_ATTRIB_GET_IV( attribs, "ora_check_sql", 13, svp, ora_check_sql);
DBD_ATTRIB_GET_IV( attribs, "ora_exe_mode", 12, svp, imp_sth->exe_mode);
DBD_ATTRIB_GET_IV( attribs, "ora_prefetch_memory", 19, svp, imp_sth->prefetch_memory);
DBD_ATTRIB_GET_IV( attribs, "ora_prefetch_rows", 17, svp, imp_sth->prefetch_rows);
DBD_ATTRIB_GET_IV( attribs, "ora_row_cache_off", 17, svp, imp_sth->row_cache_off);
DBD_ATTRIB_GET_IV( attribs, "ora_verbose", 11, svp, dbd_verbose);
DBD_ATTRIB_GET_IV( attribs, "ora_oci_success_warn", 20, svp, oci_warn);
DBD_ATTRIB_GET_IV( attribs, "ora_objects", 11, svp, ora_objects);
DBD_ATTRIB_GET_IV( attribs, "ora_ncs_buff_mtpl", 17, svp,ora_ncs_buff_mtpl);
DBD_ATTRIB_GET_IV( attribs, "RowCacheSize",12,svp, imp_sth->RowCacheSize);
if (!dbd_verbose)
DBD_ATTRIB_GET_IV( attribs, "dbd_verbose", 11, svp, dbd_verbose);
}
/* scan statement for '?', ':1' and/or ':foo' style placeholders */
if (ora_placeholders)
dbd_preparse(imp_sth, statement);
else imp_sth->statement = savepv(statement);
imp_sth->envhp = imp_dbh->envhp;
imp_sth->errhp = imp_dbh->errhp;
imp_sth->srvhp = imp_dbh->srvhp;
imp_sth->svchp = imp_dbh->svchp;
OCIHandleAlloc_ok(imp_dbh, imp_dbh->envhp, &imp_sth->stmhp, OCI_HTYPE_STMT, status);
OCIStmtPrepare_log_stat(imp_sth, imp_sth->stmhp, imp_sth->errhp,
(text*)imp_sth->statement, (ub4)strlen(imp_sth->statement),
OCI_NTV_SYNTAX, OCI_DEFAULT, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCIStmtPrepare");
OCIHandleFree_log_stat(imp_sth, imp_sth->stmhp, OCI_HTYPE_STMT, status);
return 0;
}
OCIAttrGet_stmhp_stat(imp_sth, &imp_sth->stmt_type, 0, OCI_ATTR_STMT_TYPE, status);
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_st_prepare'd sql %s ( auto_lob%d, check_sql%d)\n",
oci_stmt_type_name(imp_sth->stmt_type),
imp_sth->auto_lob, ora_check_sql);
DBIc_IMPSET_on(imp_sth);
if (ora_check_sql) {
if (!dbd_describe(sth, imp_sth))
return 0;
}
return 1;
}
sb4
dbd_phs_in(dvoid *octxp, OCIBind *bindp, ub4 iter, ub4 index,
dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp)
{
dTHX;
phs_t *phs = (phs_t*)octxp;
STRLEN phs_len;
AV *tuples_av;
SV *sv;
AV *av;
SV **sv_p;
if( bindp ){ /* For GCC not to warn on unused parameter*/ }
tuples_av = phs->imp_sth->bind_tuples;
if(tuples_av) {
/* NOTE: we already checked the validity in ora_st_bind_for_array_exec(). */
sv_p = av_fetch(tuples_av, phs->imp_sth->rowwise ? (int)iter : phs->idx, 0);
av = (AV*)SvRV(*sv_p);
sv_p = av_fetch(av, phs->imp_sth->rowwise ? phs->idx : (int)iter, 0);
sv = *sv_p;
if(SvOK(sv)) {
*bufpp = SvPV(sv, phs_len);
phs->alen = (phs->alen_incnull) ? phs_len+1 : phs_len;
phs->indp = 0;
}
else {
*bufpp = SvPVX(sv);
phs->alen = 0;
phs->indp = -1;
}
}
else
if (phs->desc_h) {
*bufpp = phs->desc_h;
phs->alen = 0;
phs->indp = 0;
}
else
if (SvOK(phs->sv)) {
*bufpp = SvPV(phs->sv, phs_len);
phs->alen = (phs->alen_incnull) ? phs_len+1 : phs_len;;
XPUSHs(sv_2mortal(newSViv(fo_type)));
XPUSHs(SvRV(cb->dbh_ref));
PUTBACK;
return_count = call_sv(cb->function, G_SCALAR);
SPAGAIN;
if (return_count != 1)
croak("Expected one scalar back from taf handler");
ret = POPi;
switch (fo_event){
case OCI_FO_BEGIN:
case OCI_FO_ABORT:
case OCI_FO_END:
case OCI_FO_REAUTH:
{
break;
}
case OCI_FO_ERROR:
{
if (ret == OCI_FO_RETRY) {
return OCI_FO_RETRY;
}
break;
}
default:
{
break;
}
}
PUTBACK;
return 0;
}
sb4
reg_taf_callback(SV *dbh, imp_dbh_t *imp_dbh)
{
dTHX;
OCIFocbkStruct tafailover;
sword status;
imp_dbh->taf_ctx.function = imp_dbh->taf_function;
imp_dbh->taf_ctx.dbh_ref = newRV_inc(dbh);
if (dbd_verbose >= 5 ) {
PerlIO_printf(DBIc_LOGPIO(imp_dbh), " In reg_taf_callback\n");
}
/* set the context up as a pointer to the taf callback struct*/
tafailover.fo_ctx = &imp_dbh->taf_ctx;
tafailover.callback_function = &taf_cbk;
/* register the callback */
OCIAttrSet_log_stat(imp_dbh, imp_dbh->srvhp, (ub4) OCI_HTYPE_SERVER,
(dvoid *) &tafailover, (ub4) 0,
(ub4) OCI_ATTR_FOCBK, imp_dbh->errhp, status);
return status;
}
#ifdef UTF8_SUPPORT
/* How many bytes are n utf8 chars in buffer */
static ub4
ora_utf8_to_bytes (ub1 *buffer, ub4 chars_wanted, ub4 max_bytes)
{
dTHX;
ub4 i = 0;
while (i < max_bytes && (chars_wanted-- > 0)) {
i += UTF8SKIP(&buffer[i]);
}
return (i < max_bytes)? i : max_bytes;
}
#if 0 /* save this for later just in case... */
/* Given the 5.6.0 implementation of utf8 handling in perl,
* avoid setting the UTF8 flag as much as possible. Almost
* every binary operator in Perl will do conversions when
* strings marked as UTF8 are involved.
* Maybe setting the flag should be default in Japan or
* Europe? Deduce that from NLS_LANG? Possibly...
*/
int
set_utf8(SV *sv) {
ub1 *c;
for (c = (ub1*)SvPVX(sv); c < (ub1*)SvEND(sv); c++) {
if (*c & 0x80) {
SvUTF8_on(sv);
return 1;
}
}
return 0;
}
#endif
#endif
/* PerlIO_printf(DBILOGFP, "lab datalen=%d long_readlen=%d bytelen=%d\n" ,datalen ,imp_sth->long_readlen, bytelen ); */
static int /* LONG and LONG RAW */
fetch_func_varfield(SV *sth, imp_fbh_t *fbh, SV *dest_sv)
{
dTHX;
D_imp_sth(sth);
D_imp_dbh_from_sth ;
D_imp_drh_from_dbh ;
fb_ary_t *fb_ary = fbh->fb_ary;
char *p = (char*)&fb_ary->abuf[0];
ub4 datalen = *(ub4*)p; /* XXX alignment ? */
p += 4;
#ifdef UTF8_SUPPORT
if (fbh->ftype == 94) {
if (datalen > imp_sth->long_readlen) {
ub4 bytelen = ora_utf8_to_bytes((ub1*)p, (ub4)imp_sth->long_readlen, datalen);
{
dTHX;
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_rebind_ph_rset phs->is_inout=%d\n",
phs->is_inout);
/* Only do this part for inout cursor refs because pp_exec_rset only gets called for all the output params */
if (phs->is_inout) {
phs->out_prepost_exec = pp_exec_rset;
return 2; /* OCI bind done */
}
else {
/* Call a special rebinder for cursor ref "in" params */
return(pp_rebind_ph_rset_in(sth, imp_sth, phs));
}
}
/* ------ */
static int
fetch_lob(SV *sth, imp_sth_t *imp_sth, OCILobLocator* lobloc, int ftype, SV *dest_sv, char *name);
static int
lob_phs_post_execute(SV *sth, imp_sth_t *imp_sth, phs_t *phs, int pre_exec)
{
dTHX;
if (pre_exec)
return 1;
/* fetch PL/SQL LOB data */
if (imp_sth->auto_lob && (
imp_sth->stmt_type == OCI_STMT_BEGIN ||
imp_sth->stmt_type == OCI_STMT_DECLARE )) {
return fetch_lob(sth, imp_sth, (OCILobLocator*) phs->desc_h, phs->ftype, phs->sv, phs->name);
}
sv_setref_pv(phs->sv, "OCILobLocatorPtr", (void*)phs->desc_h);
return 1;
}
int
dbd_rebind_ph_lob(SV *sth, imp_sth_t *imp_sth, phs_t *phs)
{
dTHX;
D_imp_dbh_from_sth ;
sword status;
ub4 lobEmpty = 0;
if (phs->desc_h && phs->desc_t == OCI_DTYPE_LOB)
ora_free_templob(sth, imp_sth, (OCILobLocator*)phs->desc_h);
if (!phs->desc_h) {
++imp_sth->has_lobs;
phs->desc_t = OCI_DTYPE_LOB;
OCIDescriptorAlloc_ok(imp_sth, imp_sth->envhp,
&phs->desc_h, phs->desc_t);
}
OCIAttrSet_log_stat(imp_sth, phs->desc_h, phs->desc_t,
&lobEmpty, 0, OCI_ATTR_LOBEMPTY, imp_sth->errhp, status);
if (status != OCI_SUCCESS)
return oci_error(sth, imp_sth->errhp, status, "OCIAttrSet OCI_ATTR_LOBEMPTY");
if (!SvPOK(phs->sv)) { /* normalizations for special cases */
if (SvOK(phs->sv)) { /* ie a number, convert to string ASAP */
if (!(SvROK(phs->sv) && phs->is_inout))
sv_2pv(phs->sv, &PL_na);
}
else { /* ensure we're at least an SVt_PV (so SvPVX etc work) */
(void)SvUPGRADE(phs->sv, SVt_PV);
}
}
phs->indp = (SvOK(phs->sv)) ? 0 : -1;
phs->progv = (char*)&phs->desc_h;
phs->maxlen = sizeof(OCILobLocator*);
if (phs->is_inout)
phs->out_prepost_exec = lob_phs_post_execute;
/* accept input LOBs */
if (sv_isobject(phs->sv) && sv_derived_from(phs->sv, "OCILobLocatorPtr")) {
OCILobLocator *src;
OCILobLocator **dest;
src = INT2PTR(OCILobLocator *, SvIV(SvRV(phs->sv)));
dest = (OCILobLocator **) phs->progv;
OCILobLocatorAssign_log_stat(imp_dbh, imp_dbh->svchp, imp_sth->errhp, src, dest, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobLocatorAssign");
return 0;
}
}
/* create temporary LOB for PL/SQL placeholder */
else if (imp_sth->stmt_type == OCI_STMT_BEGIN ||
imp_sth->stmt_type == OCI_STMT_DECLARE) {
ub4 amtp;
(void)SvUPGRADE(phs->sv, SVt_PV);
amtp = SvCUR(phs->sv); /* XXX UTF8? */
/* Create a temp lob for non-empty string */
if (amtp > 0) {
ub1 lobtype = (phs->ftype == 112 ? OCI_TEMP_CLOB : OCI_TEMP_BLOB);
OCILobCreateTemporary_log_stat(imp_dbh, imp_dbh->svchp, imp_sth->errhp,
(OCILobLocator *) phs->desc_h, (ub2) OCI_DEFAULT,
(ub1) OCI_DEFAULT, lobtype, TRUE, OCI_DURATION_SESSION, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobCreateTemporary");
return 0;
}
if( ! phs->csid ) {
ub1 csform = SQLCS_IMPLICIT;
ub2 csid = 0;
OCILobCharSetForm_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
(OCILobLocator*)phs->desc_h,
&csform,
status );
if (status != OCI_SUCCESS)
return oci_error(sth, imp_sth->errhp, status, "OCILobCharSetForm");
#ifdef OCI_ATTR_CHARSET_ID
/* Effectively only used so AL32UTF8 works properly */
OCILobCharSetId_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
(OCILobLocator*)phs->desc_h,
&csid,
status );
if (status != OCI_SUCCESS)
return oci_error(sth, imp_sth->errhp, status, "OCILobCharSetId");
#endif /* OCI_ATTR_CHARSET_ID */
/* if data is utf8 but charset isn't then switch to utf8 csid */
csid = (SvUTF8(phs->sv) && !CS_IS_UTF8(csid)) ? utf8_csid : CSFORM_IMPLIED_CSID(csform);
phs->csid = csid;
phs->csform = csform;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" calling OCILobWrite phs->csid=%d phs->csform=%d amtp=%d\n",
phs->csid, phs->csform, amtp );
/* write lob data */
OCILobWrite_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp,
(OCILobLocator*)phs->desc_h, &amtp, 1, SvPVX(phs->sv), amtp, OCI_ONE_PIECE,
0,0, phs->csid, phs->csform, status);
if (status != OCI_SUCCESS) {
return oci_error(sth, imp_sth->errhp, status, "OCILobWrite in dbd_rebind_ph_lob");
}
}
}
return 1;
}
#ifdef UTF8_SUPPORT
ub4
ora_blob_read_mb_piece(SV *sth, imp_sth_t *imp_sth, imp_fbh_t *fbh,
SV *dest_sv, long offset, ub4 len, long destoffset)
{
dTHX;
ub4 loblen = 0;
ub4 buflen;
ub4 amtp = 0;
ub4 byte_destoffset = 0;
OCILobLocator *lobl = (OCILobLocator*)fbh->desc_h;
sword ftype = fbh->ftype;
sword status;
/*
* We assume our caller has already done the
* equivalent of the following:
* (void)SvUPGRADE(dest_sv, SVt_PV);
*/
ub1 csform = SQLCS_IMPLICIT;
OCILobCharSetForm_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
lobl,
&csform,
status );
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobCharSetForm");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
if (ftype != ORA_CLOB) {
oci_error(sth, imp_sth->errhp, OCI_ERROR,
"blob_read not currently supported for non-CLOB types with OCI 8 "
"(but with OCI 8 you can set $dbh->{LongReadLen} to the length you need,"
"so you don't need to call blob_read at all)");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
OCILobGetLength_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp,
lobl, &loblen, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobGetLength ora_blob_read_mb_piece");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
loblen -= offset; /* only count from offset onwards */
amtp = (loblen > len) ? len : loblen;
buflen = 4 * amtp;
byte_destoffset = ora_utf8_to_bytes((ub1 *)(SvPVX(dest_sv)),
(ub4)destoffset, SvCUR(dest_sv));
if (loblen > 0) {
ub1 *dest_bufp;
ub1 *buffer;
New(42, buffer, buflen, ub1);
OCILobRead_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobl,
&amtp, (ub4)1 + offset, buffer, buflen,
0, 0, (ub2)0 ,csform ,status );
/* lab 0, 0, (ub2)0, (ub1)SQLCS_IMPLICIT, status); */
if (dbis->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead field %d %s: LOBlen %lu, LongReadLen %lu, "
"BufLen %lu, Got %lu\n",
fbh->field_num+1, oci_status_name(status), ul_t(loblen),
ul_t(imp_sth->long_readlen), ul_t(buflen), ul_t(amtp));
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobRead");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
amtp = ora_utf8_to_bytes(buffer, len, amtp);
SvGROW(dest_sv, byte_destoffset + amtp + 1);
dest_bufp = (ub1 *)(SvPVX(dest_sv));
dest_bufp += byte_destoffset;
memcpy(dest_bufp, buffer, amtp);
Safefree(buffer);
}
else {
assert(amtp == 0);
SvGROW(dest_sv, byte_destoffset + 1);
if (dbis->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead field %d %s: LOBlen %lu, LongReadLen %lu, "
"BufLen %lu, Got %lu\n",
fbh->field_num+1, "SKIPPED", (unsigned long)loblen,
(unsigned long)imp_sth->long_readlen, (unsigned long)buflen,
(unsigned long)amtp);
}
if (dbis->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" blob_read field %d, ftype %d, offset %ld, len %lu, "
"destoffset %ld, retlen %lu\n",
fbh->field_num+1, ftype, offset, (unsigned long) len,
destoffset, ul_t(amtp));
SvCUR_set(dest_sv, byte_destoffset+amtp);
*SvEND(dest_sv) = '\0'; /* consistent with perl sv_setpvn etc */
SvPOK_on(dest_sv);
if (ftype == ORA_CLOB && CSFORM_IMPLIES_UTF8(csform))
SvUTF8_on(dest_sv);
return 1;
}
#endif /* ifdef UTF8_SUPPORT */
ub4
ora_blob_read_piece(SV *sth, imp_sth_t *imp_sth, imp_fbh_t *fbh, SV *dest_sv,
long offset, UV len, long destoffset)
{
dTHX;
ub4 loblen = 0;
ub4 buflen;
ub4 amtp = 0;
ub1 csform = 0;
OCILobLocator *lobl = (OCILobLocator*)fbh->desc_h;
sword ftype = fbh->ftype;
sword status;
char *type_name;
if (ftype == ORA_CLOB)
type_name = "CLOB";
else if (ftype == ORA_BLOB)
type_name = "BLOB";
else if (ftype == ORA_BFILE)
type_name = "BFILE";
else {
oci_error(sth, imp_sth->errhp, OCI_ERROR,
"blob_read not currently supported for non-LOB types with OCI 8 "
"(but with OCI 8 you can set $dbh->{LongReadLen} to the length you need,"
"so you don't need to call blob_read at all)");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
OCILobGetLength_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobl, &loblen, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobGetLength ora_blob_read_piece");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
OCILobCharSetForm_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
lobl,
&csform,
status );
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobCharSetForm");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
if (ftype == ORA_CLOB && csform == SQLCS_NCHAR)
type_name = "NCLOB";
/*
* We assume our caller has already done the
* equivalent of the following:
* (void)SvUPGRADE(dest_sv, SVt_PV);
* SvGROW(dest_sv, buflen+destoffset+1);
*/
/* amtp is: LOB/BFILE CLOB/NCLOB
Input bytes characters
Output FW bytes characters (FW=Fixed Width charset, VW=Variable)
Output VW bytes characters(in), bytes returned (afterwards)
*/
amtp = (loblen > len) ? len : loblen;
/* buflen: length of buffer in bytes */
/* so for CLOBs that'll be returned as UTF8 we need more bytes that chars */
/* XXX the x4 here isn't perfect - really the code should be changed to loop */
if (ftype == ORA_CLOB && CSFORM_IMPLIES_UTF8(csform)) {
buflen = amtp * 4;
/* XXX destoffset would be counting chars here as well */
SvGROW(dest_sv, (destoffset*4) + buflen + 1);
if (destoffset) {
oci_error(sth, imp_sth->errhp, OCI_ERROR,
"blob_read with non-zero destoffset not currently supported for UTF8 values");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
}
else {
buflen = amtp;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" blob_read field %d: ftype %d %s, offset %ld, len %lu."
"LOB csform %d, len %lu, amtp %lu, (destoffset=%ld)\n",
fbh->field_num+1, ftype, type_name, offset, ul_t(len),
csform,(unsigned long) (loblen), ul_t(amtp), destoffset);
if (loblen > 0) {
ub1 * bufp = (ub1 *)(SvPVX(dest_sv));
bufp += destoffset;
OCILobRead_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobl,
&amtp, (ub4)1 + offset, bufp, buflen,
0, 0, (ub2)0 , csform, status);
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead field %d %s: LOBlen %lu, LongReadLen %lu,"
"BufLen %lu, amtp %lu\n",
fbh->field_num+1, oci_status_name(status), ul_t(loblen),
ul_t(imp_sth->long_readlen), ul_t(buflen), ul_t(amtp));
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobRead");
sv_set_undef(dest_sv); /* signal error */
return 0;
}
if (ftype == ORA_CLOB && CSFORM_IMPLIES_UTF8(csform))
SvUTF8_on(dest_sv);
}
else {
assert(amtp == 0);
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead field %d %s: LOBlen %lu, LongReadLen %lu, "
"BufLen %lu, Got %lu\n",
fbh->field_num+1, "SKIPPED", ul_t(loblen),
ul_t(imp_sth->long_readlen), ul_t(buflen), ul_t(amtp));
}
/*
* We assume our caller will perform
* the equivalent of the following:
* SvCUR(dest_sv) = amtp;
* *SvEND(dest_sv) = '\0';
* SvPOK_on(dest_sv);
*/
return(amtp);
}
static int
fetch_lob(SV *sth, imp_sth_t *imp_sth, OCILobLocator* lobloc, int ftype, SV *dest_sv, char *name)
{
dTHX;
ub4 loblen = 0;
ub4 buflen = 0;
ub4 amtp = 0;
sword status;
if (!name)
name = "an unknown field";
/* this function is not called for NULL lobs */
/* The length is expressed in terms of bytes for BLOBs and BFILEs, */
/* and in terms of characters for CLOBs and NCLOBS */
OCILobGetLength_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobloc, &loblen, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobGetLength fetch_lob");
return 0;
}
if (loblen > imp_sth->long_readlen) { /* LOB will be truncated */
int oraperl = DBIc_COMPAT(imp_sth);
D_imp_dbh_from_sth ;
D_imp_drh_from_dbh ;
/* move setting amtp up to ensure error message OK */
amtp = imp_sth->long_readlen;
if (DBIc_has(imp_sth,DBIcf_LongTruncOk) || (oraperl && SvIV(imp_drh -> ora_trunc))) {
/* user says truncation is ok */
/* Oraperl recorded the truncation in ora_errno so we */
/* so also but only for Oraperl mode handles. */
if (oraperl) sv_setiv(DBIc_ERR(imp_sth), 1406);
}
else {
char buf[300];
sprintf(buf,"fetching %s. LOB value truncated from %ld to %ld. %s",
name, ul_t(loblen), ul_t(amtp),
"DBI attribute LongReadLen too small and/or LongTruncOk not set");
oci_error_err(sth, NULL, OCI_ERROR, buf, 24345); /* appropriate ORA error number */
sv_set_undef(dest_sv);
return 0;
}
}
else
amtp = loblen;
(void)SvUPGRADE(dest_sv, SVt_PV);
/* XXXX I've hacked on this and left it probably broken
because I didn't have time to research which args to OCI funcs need
to be in char or byte units. That still needs to be done.
better variable names may help.
(The old version (1.15) duplicated too much code here because
I applied a contributed patch that wasn't ideal, I had too little time
to sort it out.)
Whatever is done here, similar changes are probably needed for the
ora_lob_*() methods when handling CLOBs.
*/
/* Yep you did bust it good and bad. Seem that when the charset of
the client and the DB are comptiable the buflen and amtp are both in chars
no matter how many bytes make up the chars. If it is the case were the Client's
NLS_LANG or NLS_NCHAR is not a subset of the Server's the server will try to traslate
the data to the Client's wishes and that is wen it uses will send the ampt value will be in bytes*/
buflen = amtp;
if (ftype == ORA_CLOB)
buflen = buflen*ora_ncs_buff_mtpl;
SvGROW(dest_sv, buflen+1);
if (loblen > 0) {
ub1 csform = 0;
OCILobCharSetForm_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
lobloc,
&csform,
status );
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobCharSetForm");
sv_set_undef(dest_sv);
return 0;
}
if (ftype == ORA_BFILE) {
OCILobFileOpen_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobloc,
(ub1)OCI_FILE_READONLY, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobFileOpen");
sv_set_undef(dest_sv);
return 0;
}
}
OCILobRead_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, lobloc,
&amtp, (ub4)1, SvPVX(dest_sv), buflen,
0, 0, (ub2)0, csform, status);
if (status != OCI_SUCCESS ) {
if (status == OCI_NEED_DATA ){
char buf[300];
sprintf(buf,"fetching %s. LOB and the read bufer is only %lubytes, and the ora_ncs_buff_mtpl is %d, which is too small. Try setting ora_ncs_buff_mtpl to %d",
name, (unsigned long)buflen, ora_ncs_buff_mtpl,ora_ncs_buff_mtpl+1);
oci_error_err(sth, NULL, OCI_ERROR, buf, OCI_NEED_DATA); /* appropriate ORA error number */
/*croak("DBD::Oracle has returned a %s status when doing a LobRead!! \n",oci_status_name(status));*/
/*why a croak here well if it goes on it will result in a
ORA-03127: no new operations allowed until the active operation ends
This will result in a crash if there are any other fetchst*/
}
else {
oci_error(sth, imp_sth->errhp, status, "OCILobRead");
sv_set_undef(dest_sv);
}
return 0;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 || oci_warn){
char buf[11];
strcpy(buf,"bytes");
if (ftype == ORA_CLOB)
strcpy(buf,"characters");
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead %s %s: csform %d (%s), LOBlen %lu(%s), "
"LongReadLen %lu(%s), BufLen %lu(%s), Got %lu(%s)\n",
name, oci_status_name(status), csform, oci_csform_name(csform),
ul_t(loblen),buf ,
ul_t(imp_sth->long_readlen),buf, ul_t(buflen),buf, ul_t(amtp),buf);
}
if (ftype == ORA_BFILE) {
OCILobFileClose_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp,
lobloc, status);
}
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCILobFileClose");
sv_set_undef(dest_sv);
return 0;
}
/* tell perl what we've put in its dest_sv */
SvCUR(dest_sv) = amtp;
*SvEND(dest_sv) = '\0';
if (ftype == ORA_CLOB && CSFORM_IMPLIES_UTF8(csform)) /* Don't set UTF8 on BLOBs */
SvUTF8_on(dest_sv);
ora_free_templob(sth, imp_sth, lobloc);
}
else { /* LOB length is 0 */
assert(amtp == 0);
/* tell perl what we've put in its dest_sv */
SvCUR(dest_sv) = amtp;
*SvEND(dest_sv) = '\0';
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" OCILobRead %s %s: LOBlen %lu, LongReadLen %lu, "
"BufLen %lu, Got %lu\n",
name, "SKIPPED", ul_t(loblen),
ul_t(imp_sth->long_readlen), ul_t(buflen), ul_t(amtp));
}
SvPOK_on(dest_sv);
return 1;
}
static int
fetch_func_autolob(SV *sth, imp_fbh_t *fbh, SV *dest_sv)
{
dTHX;
char name[64];
sprintf(name, "field %d of %d", fbh->field_num, DBIc_NUM_FIELDS(fbh->imp_sth));
return fetch_lob(sth, fbh->imp_sth, (OCILobLocator*)fbh->desc_h, fbh->ftype, dest_sv, name);
}
static int
fetch_func_getrefpv(SV *sth, imp_fbh_t *fbh, SV *dest_sv)
{
dTHX;
if( sth ) { /* For GCC not to warn on unused parameter */ }
/* See the Oracle::OCI module for how to actually use this! */
sv_setref_pv(dest_sv, fbh->bless, (void*)fbh->desc_h);
return 1;
}
#ifdef OCI_DTYPE_REF
static void
fbh_setup_getrefpv(imp_sth_t *imp_sth, imp_fbh_t *fbh, int desc_t, char *bless)
{
dTHX;
else{
if (cache_rows == 0) { /* automatically size the cache */
/* automatically size the cache */
/* Oracle packets on ethernet have max size of around 1460. */
/* We'll aim to fill our row cache with around 10 per go. */
/* Using 10 means any 'runt' packets will have less impact. */
/* orginally set up as above but playing around with newer versions*/
/* I found that 500 was much faster*/
int txfr_size = 10 * 1460; /* desired transfer/cache size */
cache_rows = txfr_size / est_width; /* (maybe 1 or 0) */
/* To ensure good performance with large rows (near or larger */
/* than our target transfer size) we set a minimum cache size. */
/* I made them all at least 10* what they were before this */
/* main reasoning this old value reprewneted a norm in the oralce 7~8 */
/* 9 to 11 can handel much much more */
if (cache_rows < 60) /* is cache a 'useful' size? */
cache_rows = (cache_rows > 0) ? 60 : 40;
}
}
if (cache_rows > 10000000) /* keep within Oracle's limits */
cache_rows = 10000000; /* seems it was ub2 at one time now ub4 this number is arbitary on my part*/
return cache_rows;
}
/* called by get_object to return the actual value in the property */
static void get_attr_val(SV *sth,AV *list,imp_fbh_t *fbh, text *name , OCITypeCode typecode, dvoid *attr_value )
{
dTHX;
D_imp_sth(sth);
text str_buf[200];
double dnum;
size_t str_len;
ub4 ub4_str_len;
OCIRaw *raw = (OCIRaw *) 0;
OCIString *vs = (OCIString *) 0;
ub1 *temp = (ub1 *)0;
ub4 rawsize = 0;
ub4 i = 0;
sword status;
SV *raw_sv;
/* get the data based on the type code*/
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth),
" getting value of object attribute named %s with typecode=%s\n",
name,oci_typecode_name(typecode));
}
switch (typecode)
{
case OCI_TYPECODE_INTERVAL_YM :
case OCI_TYPECODE_INTERVAL_DS :
OCIIntervalToText_log_stat(fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
attr_value,
str_buf,
(size_t) 200,
&str_len,
status);
str_buf[str_len+1] = '\0';
av_push(list, newSVpv( (char *) str_buf,0));
break;
case OCI_TYPECODE_TIMESTAMP_TZ :
case OCI_TYPECODE_TIMESTAMP_LTZ :
case OCI_TYPECODE_TIMESTAMP :
ub4_str_len = 200;
OCIDateTimeToText_log_stat(fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
attr_value,
&ub4_str_len,
str_buf,
status);
if (typecode == OCI_TYPECODE_TIMESTAMP_TZ || typecode == OCI_TYPECODE_TIMESTAMP_LTZ){
char s_tz_hour[3]="000";
char s_tz_min[3]="000";
sb1 tz_hour;
sb1 tz_minute;
status = OCIDateTimeGetTimeZoneOffset (fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
*(OCIDateTime**)attr_value,
&tz_hour,
&tz_minute );
if ( (tz_hour<0) && (tz_hour>-10) ){
sprintf(s_tz_hour," %03d",tz_hour);
} else {
sprintf(s_tz_hour," %02d",tz_hour);
}
sprintf(s_tz_min,":%02d", tz_minute);
strcat((signed char*)str_buf, s_tz_hour);
strcat((signed char*)str_buf, s_tz_min);
str_buf[ub4_str_len+7] = '\0';
} else {
str_buf[ub4_str_len+1] = '\0';
}
av_push(list, newSVpv( (char *) str_buf,0));
break;
case OCI_TYPECODE_DATE : /* fixed length string*/
ub4_str_len = 200;
OCIDateToText_log_stat(fbh->imp_sth,
fbh->imp_sth->errhp,
(CONST OCIDate *) attr_value,
&ub4_str_len,
str_buf,
status);
str_buf[ub4_str_len+1] = '\0';
av_push(list, newSVpv( (char *) str_buf,0));
break;
case OCI_TYPECODE_CLOB:
case OCI_TYPECODE_BLOB:
case OCI_TYPECODE_BFILE:
raw_sv = newSV(0);
fetch_lob(sth, fbh->imp_sth,*(OCILobLocator**)attr_value, typecode, raw_sv, (signed char*)name);
av_push(list, raw_sv);
break;
case OCI_TYPECODE_RAW :/* RAW*/
raw_sv = newSV(0);
raw = *(OCIRaw **) attr_value;
temp = OCIRawPtr(fbh->imp_sth->envhp, raw);
rawsize = OCIRawSize (fbh->imp_sth->envhp, raw);
for (i=0; i < rawsize; i++) {
sv_catpvf(raw_sv,"0x%x ", temp[i]);
}
sv_catpv(raw_sv,"\n");
av_push(list, raw_sv);
break;
case OCI_TYPECODE_CHAR : /* fixed length string */
case OCI_TYPECODE_VARCHAR : /* varchar */
case OCI_TYPECODE_VARCHAR2 : /* varchar2 */
vs = *(OCIString **) attr_value;
av_push(list, newSVpv((char *) OCIStringPtr(fbh->imp_sth->envhp, vs),0));
break;
case OCI_TYPECODE_SIGNED8 : /* BYTE - sb1 */
av_push(list, newSVuv(*(sb1 *)attr_value));
break;
case OCI_TYPECODE_UNSIGNED8 : /* UNSIGNED BYTE - ub1 */
av_push(list, newSViv(*(ub1 *)attr_value));
break;
case OCI_TYPECODE_OCTET : /* OCT*/
av_push(list, newSViv(*(ub1 *)attr_value));
break;
case OCI_TYPECODE_UNSIGNED16 : /* UNSIGNED SHORT */
case OCI_TYPECODE_UNSIGNED32 : /* UNSIGNED LONG */
case OCI_TYPECODE_REAL : /* REAL */
case OCI_TYPECODE_DOUBLE : /* DOUBLE */
case OCI_TYPECODE_INTEGER : /* INT */
case OCI_TYPECODE_SIGNED16 : /* SHORT */
case OCI_TYPECODE_SIGNED32 : /* LONG */
case OCI_TYPECODE_DECIMAL : /* DECIMAL */
case OCI_TYPECODE_FLOAT : /* FLOAT */
case OCI_TYPECODE_NUMBER : /* NUMBER */
case OCI_TYPECODE_SMALLINT : /* SMALLINT */
objref = newRV_noinc((SV*) self);
objref = sv_bless(objref, gv_stashpv("DBD::Oracle::Object", 0));
}
return objref;
}
/*gets the properties of an object from a fetch by using the attributes saved in the describe */
int
get_object (SV *sth, AV *list, imp_fbh_t *fbh,fbh_obj_t *base_obj,OCIComplexObject *value, OCIType *instance_tdo, dvoid *obj_ind){
dTHX;
D_imp_sth(sth);
sword status;
dvoid *element ;
dvoid *attr_value;
boolean eoc;
ub2 pos;
dvoid *attr_null_struct;
OCIInd attr_null_status;
OCIInd *element_null;
OCIType *attr_tdo;
OCIIter *itr;
fbh_obj_t *fld;
fbh_obj_t *obj = base_obj;
OCIType *tdo = instance_tdo ? instance_tdo : obj->tdo;
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth),
" getting attributes of object named %s with typecode=%s\n",
obj->type_name,oci_typecode_name(obj->typecode));
}
switch (obj->typecode) {
case OCI_TYPECODE_OBJECT: /* embedded ADT */
case OCI_TYPECODE_OPAQUE: /*doesn't do anything though*/
if (ora_objects){
sword status;
if (!instance_tdo && !obj->is_final_type) {
OCIRef *type_ref=0;
status = OCIObjectNew(fbh->imp_sth->envhp, fbh->imp_sth->errhp, fbh->imp_sth->svchp,
OCI_TYPECODE_REF, (OCIType *)0,
(dvoid *)0, OCI_DURATION_DEFAULT, TRUE,
(dvoid **) &type_ref);
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCIObjectNew");
return 0;
}
status=OCIObjectGetTypeRef(fbh->imp_sth->envhp,fbh->imp_sth->errhp, (dvoid*)value, type_ref);
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCIObjectGetTypeRef");
return 0;
}
OCITypeByRef_log_stat(fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
type_ref,
&tdo,status);
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCITypeByRef");
return 0;
}
status = OCIObjectFree(fbh->imp_sth->envhp, fbh->imp_sth->errhp, type_ref, (ub2)0);
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCIObjectFree");
return 0;
}
}
if (tdo != obj->tdo) {
/* this is subtype -> search for subtype obj */
while (obj->next_subtype && tdo != obj->tdo) {
obj = obj->next_subtype;
}
if (tdo != obj->tdo) {
/* new subtyped -> get obj description */
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth), " describe subtype (tdo=%p) of object type %s (tdo=%p)\n",(void*)tdo,base_obj->type_name,(void*)base_obj->tdo);
}
Newz(1, obj->next_subtype, 1, fbh_obj_t);
obj->next_subtype->tdo = tdo;
if ( describe_obj_by_tdo(sth, fbh->imp_sth, obj->next_subtype, 0 /*unknown level there*/) ) {
obj = obj->next_subtype;
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ){
dump_struct(fbh->imp_sth,obj,0);
}
}
else {
obj->next_subtype = 0;
}
}
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth), " getting attributes of object subtype %s\n",obj->type_name);
}
}
av_push(list, newSVpv((char*)obj->type_name, obj->type_namel));
}
for (pos = 0; pos < obj->field_count; pos++){
fld = &obj->fields[pos]; /*get the field */
if (ora_objects) {
/* add field name */
av_push(list, newSVpv((char*)fld->type_name, fld->type_namel));
}
/*
the little bastard above took me ages to find out
seems Oracle does not like people to know that it can do this
the concept is simple really
1. pin the object
2. bind with dty = SQLT_NTY
3. OCIDefineObject using the TDO
4. one gets the null indicator of the objcet with OCIObjectGetInd
The the obj_ind is for the entier object not the properties so you call it once it
gets all of the indicators for the objects so you pass it into OCIObjectGetAttr and that
function will set attr_null_status as in the get below.
5. interate over the attributes of the object
The thing to remember is that OCI and C have no way of representing a DB NULLs so we use the OCIInd find out
if the object or any of its properties are NULL, This is one little line in a 20 chapter book and even then
id only shows you examples with the C struct built in and only a single record. Nowhere does it say you can do it this way.
*/
OCIObjectGetAttr_log_stat(
fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
value, /* instance */
obj_ind, /* null_struct */
tdo, /* tdo */
(CONST oratext**)&fld->type_name, /* names */
&fld->type_namel, /* lengths */
1, /* name_count */
(ub4 *)0, /* indexes */
0, /* index_count */
&attr_null_status, /* attr_null_status */
&attr_null_struct, /* attr_null_struct */
&attr_value, /* attr_value */
&attr_tdo, /* attr_tdo */
status);
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCIObjectGetAttr");
return 0;
}
if (attr_null_status==OCI_IND_NULL){
av_push(list, &PL_sv_undef);
} else {
if (fld->typecode == OCI_TYPECODE_OBJECT || fld->typecode == OCI_TYPECODE_VARRAY || fld->typecode == OCI_TYPECODE_TABLE || fld->typecode == OCI_TYPECODE_NAMEDCOLLECTION){
fld->fields[0].value = newAV();
if (fld->typecode != OCI_TYPECODE_OBJECT)
attr_value = *(dvoid **)attr_value;
if (!get_object (sth,fld->fields[0].value, fbh, &fld->fields[0],attr_value, attr_tdo, attr_null_struct))
return 0;
av_push(list, new_ora_object(fld->fields[0].value, fld->typecode));
} else{ /* else, display the scaler type attribute */
get_attr_val(sth,list, fbh, fld->type_name, fld->typecode, attr_value);
}
}
}
break;
case OCI_TYPECODE_REF : /* embedded ADT */
croak("panic: OCI_TYPECODE_REF objets () are not supported ");
break;
case OCI_TYPECODE_NAMEDCOLLECTION : /*this works for both as I am using CONST OCIColl */
switch (obj->col_typecode) { /*there may be more thatn two I havn't found them yet mmight be XML??*/
case OCI_TYPECODE_TABLE : /* nested table */
case OCI_TYPECODE_VARRAY : /* variable array */
fld = &obj->fields[0]; /*get the field */
OCIIterCreate_log_stat(fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
(OCIColl*) value,
&itr,
status);
if (status != OCI_SUCCESS) {
/*not really an error just no data
oci_error(sth, fbh->imp_sth->errhp, status, "OCIIterCreate");*/
status = OCI_SUCCESS;
av_push(list, &PL_sv_undef);
return 0;
}
for(eoc = FALSE;!OCIIterNext(fbh->imp_sth->envhp, fbh->imp_sth->errhp, itr,
(dvoid **) &element,
(dvoid **) &element_null, &eoc) && !eoc;)
{
if (*element_null==OCI_IND_NULL){
av_push(list, &PL_sv_undef);
} else {
if (obj->element_typecode == OCI_TYPECODE_OBJECT || obj->element_typecode == OCI_TYPECODE_VARRAY || obj->element_typecode== OCI_TYPECODE_TABLE || obj->element_typecode== OCI_TYPECODE_NAMEDCOLLECTION){
fld->value = newAV();
if(!get_object (sth,fld->value, fbh, fld,element,0,element_null))
return 0;
av_push(list, new_ora_object(fld->value, obj->element_typecode));
} else{ /* else, display the scaler type attribute */
get_attr_val(sth,list, fbh, obj->type_name, obj->element_typecode, element);
}
}
}
/*nasty surprise here. one has to get rid of the iterator or you will leak memory
not documented in oci or in demos */
OCIIterDelete_log_stat(fbh->imp_sth,
fbh->imp_sth->envhp,
fbh->imp_sth->errhp,
&itr,
status );
if (status != OCI_SUCCESS) {
oci_error(sth, fbh->imp_sth->errhp, status, "OCIIterDelete");
return 0;
}
break;
default:
break;
}
break;
default:
if (value) {
get_attr_val(sth,list, fbh, obj->type_name, obj->typecode, value);
}
else
return 1;
break;
}
return 1;
}
/*cutsom fetch for embedded objects */
static int
fetch_func_oci_object(SV *sth, imp_fbh_t *fbh,SV *dest_sv)
{
dTHX;
D_imp_sth(sth);
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth),
" getting an embedded object named %s with typecode=%s\n",
fbh->obj->type_name,oci_typecode_name(fbh->obj->typecode));
}
if (fbh->obj->obj_ind && fbh->obj->obj_ind[0] == OCI_IND_NULL) {
sv_set_undef(dest_sv);
return 1;
}
fbh->obj->value=newAV();
/*will return referance to an array of scalars*/
if (!get_object(sth,fbh->obj->value,fbh,fbh->obj,fbh->obj->obj_value,0,fbh->obj->obj_ind)){
return 0;
} else {
sv_setsv(dest_sv, sv_2mortal(new_ora_object(fbh->obj->value, fbh->obj->typecode)));
return 1;
}
}
static int
fb_ary_t *fb_ary = fbh->fb_ary;
ub4 actual_bufl=imp_sth->piece_size*(fb_ary->piece_count)+fb_ary->bufl;
if (fb_ary->piece_count==0){
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Fetch persistent lob of %d (char/bytes) with callback in 1 "
"piece of %d (Char/Bytes)\n",
actual_bufl,fb_ary->bufl);
memcpy(fb_ary->cb_abuf,fb_ary->abuf,fb_ary->bufl );
} else {
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Fetch persistent lob of %d (Char/Bytes) with callback in %d "
"piece(s) of %d (Char/Bytes) and one piece of %d (Char/Bytes)\n",
actual_bufl,fb_ary->piece_count,fbh->piece_size,fb_ary->bufl);
memcpy(fb_ary->cb_abuf+imp_sth->piece_size*(fb_ary->piece_count),fb_ary->abuf,fb_ary->bufl );
}
if (fbh->ftype == SQLT_BIN){
*(fb_ary->cb_abuf+(actual_bufl))='\0'; /* add a null teminator*/
sv_setpvn(dest_sv, (char*)fb_ary->cb_abuf,(STRLEN)actual_bufl);
} else {
sv_setpvn(dest_sv, (char*)fb_ary->cb_abuf,(STRLEN)actual_bufl);
if (CSFORM_IMPLIES_UTF8(fbh->csform) ){
SvUTF8_on(dest_sv);
}
}
return 1;
}
/* This is another way to get lobs as a alternate to callback */
static int
fetch_get_piece(SV *sth, imp_fbh_t *fbh,SV *dest_sv)
{
dTHX;
D_imp_sth(sth);
fb_ary_t *fb_ary = fbh->fb_ary;
ub4 buflen = fb_ary->bufl;
ub4 actual_bufl = 0;
ub1 piece = OCI_FIRST_PIECE;
void *hdlptr = (dvoid *) 0;
ub4 hdltype = OCI_HTYPE_DEFINE, iter = 0, idx = 0;
ub1 in_out = 0;
sb2 indptr = 0;
ub2 rcode = 0;
sword status = OCI_NEED_DATA;
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 ) {
PerlIO_printf(DBIc_LOGPIO(imp_sth), "in fetch_get_piece \n");
}
while (status == OCI_NEED_DATA){
OCIStmtGetPieceInfo_log_stat(fbh->imp_sth,
fbh->imp_sth->stmhp,
fbh->imp_sth->errhp,
&hdlptr,
&hdltype,
&in_out,
&iter,
&idx,
&piece,
status);
/* This is how this works
First we get the piece Info above
the bugger thing is that this will get the piece info in sequential order so on each call to the above
you have to check to ensure you have the right define handle from the OCIDefineByPos
I do it in the next if statement. So this will loop untill the handle changes at that point it exits the loop
during the loop I add the abuf to the cb_abuf using the buflen that is set above.
I get the actual buffer length by adding up all the pieces (buflen) as I go along
Another really anoying thing is once can only find out if there is data left over at the very end of the fetching of the colums
so I make it warn if the LongTruncOk. I could also do this before but that would not result in any of the good data getting
in
*/
if ( hdlptr==fbh->defnp){
OCIStmtSetPieceInfo_log_stat(fbh->imp_sth,
fbh->defnp,
fbh->imp_sth->errhp,
fb_ary->abuf,
&buflen,
piece,
(dvoid *)&indptr,
&rcode,status);
OCIStmtFetch_log_stat(fbh->imp_sth, fbh->imp_sth->stmhp,fbh->imp_sth->errhp,1,(ub2)OCI_FETCH_NEXT,OCI_DEFAULT,status);
if (status==OCI_SUCCESS_WITH_INFO && !DBIc_has(fbh->imp_sth,DBIcf_LongTruncOk)){
dTHR; /* for DBIc_ACTIVE_off */
DBIc_ACTIVE_off(fbh->imp_sth); /* eg finish */
oci_error(sth, fbh->imp_sth->errhp, status, "OCIStmtFetch, LongReadLen too small and/or LongTruncOk not set");
}
memcpy(fb_ary->cb_abuf+fb_ary->piece_count*imp_sth->piece_size,fb_ary->abuf,buflen );
fb_ary->piece_count++;/*used to tell me how many pieces I have, for debuffing in this case */
actual_bufl=actual_bufl+buflen;
}else {
status=OCI_LAST_PIECE;
}
}
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 ){
if (fb_ary->piece_count==1){
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Fetch persistent lob of %d (Char/Bytes) with Polling "
"in 1 piece\n",
actual_bufl);
} else {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Fetch persistent lob of %d (Char/Bytes) with Polling "
"in %d piece(s) of %d (Char/Bytes) and one piece of %d (Char/Bytes)\n",
actual_bufl,fb_ary->piece_count,fbh->piece_size,buflen);
}
}
if (actual_bufl > 0){
sv_setpvn(dest_sv, (char*)fb_ary->cb_abuf,(STRLEN)actual_bufl);
if (fbh->ftype != SQLT_BIN){
if (CSFORM_IMPLIES_UTF8(fbh->csform) ){ /* do the UTF 8 magic*/
SvUTF8_on(dest_sv);
}
}
} else {
sv_set_undef(dest_sv);
}
return 1;
}
int
empty_oci_object(fbh_obj_t *obj){
dTHX;
int pos =0;
fbh_obj_t *fld=NULL;
switch (obj->element_typecode) {
precideace
From DBI POD
A hint to the driver indicating the size of the local
row cache that the application would like the driver to
use for future SELECT statements.
so RowCacheSize is for a local cache to cut down on round trips
The OCI doc state that both OCI_ATTR_PREFETCH_ROWS OCI_ATTR_PREFETCH_MEMORY
sets up a cleint side cache but in earlier version than 1.24 we only selected
one record at a time from the fetch this means a round trip (at least to the local cache)
at each fetch.
With the new array fetch we truly have a local cache so I will use it
RowCacheSize to set the value of that cache or the array fetch*/
/* number of rows to cache if using oraperl will leave this in for now*/
if (SvOK(imp_drh->ora_cache_o)){
imp_sth->cache_rows = SvIV(imp_drh->ora_cache_o);
}
else if (SvOK(imp_drh->ora_cache)){
imp_sth->cache_rows = SvIV(imp_drh->ora_cache);
}
prefetch_rows =imp_sth->prefetch_rows;
prefetch_mem =imp_sth->prefetch_memory;
if (!cache_rows) { /*start with this value if not set then set default cache */
cache_rows=calc_cache_rows(imp_sth->cache_rows,(int)num_fields, imp_sth->est_width, has_longs,0);
if(!prefetch_rows && !prefetch_mem){ /*if there are not prefetch rows make sure I set it here to the default*/
prefetch_rows=cache_rows;
}
}
else if (imp_dbh->RowCacheSize < 0) {/* for compaibility with DBI doc negitive value here means use the value as memory*/
prefetch_mem =-imp_dbh->RowCacheSize; /* cache_mem always +ve here */
prefetch_rows =0;
cache_rows=calc_cache_rows(imp_sth->cache_rows,(int)num_fields, imp_sth->est_width, has_longs,prefetch_mem);
/*The above fucntion will set the cache_rows using memory as the limit*/
}
else {
if (!prefetch_mem){
prefetch_rows = cache_rows; /*use the RowCacheSize*/
}
}
if (cache_rows <= prefetch_rows){
cache_rows=prefetch_rows;
/* is prefetch_rows are greater than the RowCahceSize then use prefetch_rows*/
}
OCIAttrSet_log_stat(imp_sth, imp_sth->stmhp, OCI_HTYPE_STMT,
&prefetch_mem, sizeof(prefetch_mem), OCI_ATTR_PREFETCH_MEMORY,
imp_sth->errhp, status);
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status,
"OCIAttrSet OCI_ATTR_PREFETCH_MEMORY");
++num_errors;
}
OCIAttrSet_log_stat(imp_sth, imp_sth->stmhp, OCI_HTYPE_STMT,
&prefetch_rows, sizeof(prefetch_rows), OCI_ATTR_PREFETCH_ROWS,
imp_sth->errhp, status);
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status, "OCIAttrSet OCI_ATTR_PREFETCH_ROWS");
++num_errors;
}
imp_sth->rs_array_size=cache_rows;
if (max_cache_rows){/* limited to 1 by a cursor or something else*/
imp_sth->rs_array_size=1;
}
if (imp_sth->row_cache_off){/*set the size of the Rows in Cache value*/
imp_dbh->RowsInCache =1;
imp_sth->RowsInCache =1;
}
else {
imp_dbh->RowsInCache=imp_sth->rs_array_size;
imp_sth->RowsInCache=imp_sth->rs_array_size;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 || oci_warn) /*will also display if oci_warn is on*/
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" cache settings DB Handle RowCacheSize=%d,Statement Handle "
"RowCacheSize=%d, OCI_ATTR_PREFETCH_ROWS=%lu, "
"OCI_ATTR_PREFETCH_MEMORY=%lu, Rows per Fetch=%d, Multiple Row Fetch=%s\n",
imp_dbh->RowCacheSize, imp_sth->RowCacheSize,
(unsigned long) (prefetch_rows), (unsigned long) (prefetch_mem),
cache_rows,(imp_sth->row_cache_off)?"Off":"On");
return num_errors;
}
/*recurses down the field's TDOs and saves the little bits it need for later use on a fetch fbh->obj */
int
describe_obj(SV *sth,imp_sth_t *imp_sth,OCIParam *parm,fbh_obj_t *obj,int level )
{
dTHX;
sword status;
OCIRef *type_ref;
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"At level=%d in description an embedded object \n",level);
}
/*Describe the field (OCIParm) we know it is a object or a collection */
/* Get the Actual TDO */
OCIAttrGet_parmdp(imp_sth,parm, &type_ref, 0, OCI_ATTR_REF_TDO, status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
OCITypeByRef_log_stat(imp_sth,
imp_sth->envhp,
imp_sth->errhp,
type_ref,
&obj->tdo,
status);
if (status != OCI_SUCCESS) {
oci_error(sth, imp_sth->errhp, status, "OCITypeByRef");
return 0;
}
return describe_obj_by_tdo(sth, imp_sth, obj, level);
}
int
describe_obj_by_tdo(SV *sth,imp_sth_t *imp_sth,fbh_obj_t *obj,ub2 level ) {
dTHX;
sword status;
text *type_name, *schema_name;
ub4 type_namel, schema_namel;
OCIDescribeAny_log_stat(imp_sth, imp_sth->svchp,imp_sth->errhp,obj->tdo,(ub4)0,OCI_OTYPE_PTR,(ub1)1,OCI_PTYPE_TYPE,imp_sth->dschp,status);
/*we have the Actual TDO so lets see what it is made up of by a describe*/
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIDescribeAny");
return 0;
}
OCIAttrGet_parmap(imp_sth, imp_sth->dschp,OCI_HTYPE_DESCRIBE, &obj->parmdp, 0, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
/*and we store it in the object's paramdp for now*/
OCIAttrGet_parmdp(imp_sth, obj->parmdp, &schema_name, &schema_namel, OCI_ATTR_SCHEMA_NAME, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
OCIAttrGet_parmdp(imp_sth, obj->parmdp, &type_name, &type_namel, OCI_ATTR_NAME, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
/* make full type_name: schema_name + "." + type_name */
obj->full_type_name = newSVpv((char*)schema_name, schema_namel);
sv_catpvn(obj->full_type_name, ".", 1);
sv_catpvn(obj->full_type_name, (char*)type_name, type_namel);
obj->type_name = (text*)SvPV(obj->full_type_name,PL_na);
/*we need to know its type code*/
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->typecode, 0, OCI_ATTR_TYPECODE, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Getting the properties of object named =%s at level %d typecode=%d\n",
obj->type_name,level,obj->typecode);
}
if (obj->typecode == OCI_TYPECODE_OBJECT || obj->typecode == OCI_TYPECODE_OPAQUE){
OCIParam *list_attr= (OCIParam *) 0;
ub2 pos;
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Object named =%s at level %d is an Object\n",
obj->type_name,level);
}
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->obj_ref, 0, OCI_ATTR_REF_TDO, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
/*we will need a reff to the TDO for the pin operation*/
OCIObjectPin_log_stat(imp_sth, imp_sth->envhp,imp_sth->errhp, obj->obj_ref,(dvoid **)&obj->obj_type,status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIObjectPin");
return 0;
}
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->is_final_type,(ub4 *) 0, OCI_ATTR_IS_FINAL_TYPE, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->field_count,(ub4 *) 0, OCI_ATTR_NUM_TYPE_ATTRS, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
/*now get the differnt fields of this object add one field object for property*/
Newz(1, obj->fields, (unsigned) obj->field_count, fbh_obj_t);
/*a field is just another instance of an obj not a new struct*/
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&list_attr,(ub4 *) 0, OCI_ATTR_LIST_TYPE_ATTRS, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
for (pos = 1; pos <= obj->field_count; pos++){
OCIParam *parmdf= (OCIParam *) 0;
fbh_obj_t *fld = &obj->fields[pos-1]; /*get the field holder*/
OCIParamGet_log_stat(imp_sth, (dvoid *) list_attr,(ub4) OCI_DTYPE_PARAM, imp_sth->errhp,(dvoid *)&parmdf, (ub4) pos ,status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIParamGet");
return 0;
}
OCIAttrGet_parmdp(imp_sth, (dvoid*)parmdf, (dvoid *)&fld->type_name,(ub4 *) &fld->type_namel, OCI_ATTR_NAME, status);
/* get the name of the attribute */
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
OCIAttrGet_parmdp(imp_sth, (dvoid*)parmdf, (void *)&fld->typecode,(ub4 *) 0, OCI_ATTR_TYPECODE, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Getting property #%d, named=%s and its typecode is %d \n",
pos, fld->type_name, fld->typecode);
}
if (fld->typecode == OCI_TYPECODE_OBJECT || fld->typecode == OCI_TYPECODE_VARRAY || fld->typecode == OCI_TYPECODE_TABLE || fld->typecode == OCI_TYPECODE_NAMEDCOLLECTION){
/*this is some sort of object or collection so lets drill down some more*/
Newz(1, fld->fields, 1, fbh_obj_t);
fld->field_count=1;/*not really needed but used internally*/
status=describe_obj(sth,imp_sth,parmdf,fld->fields,level+1);
}
}
} else {
/*well this is an embedded table or varray of some form so find out what is in it*/
if (DBIc_DBISTATE(imp_sth)->debug >= 6 || dbd_verbose >= 6 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Object named =%s at level %d is an Varray or Table\n",
obj->type_name,level);
}
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->col_typecode, 0, OCI_ATTR_COLLECTION_TYPECODE, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
/* first get what sort of collection it is by coll typecode*/
OCIAttrGet_parmdp(imp_sth, obj->parmdp, (dvoid *)&obj->parmap, 0, OCI_ATTR_COLLECTION_ELEMENT, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIAttrGet");
return 0;
}
return 1;
}
int
dbd_describe(SV *h, imp_sth_t *imp_sth)
{
dTHX;
D_imp_dbh_from_sth;
D_imp_drh_from_dbh;
UV long_readlen;
ub4 num_fields;
int num_errors = 0;
int has_longs = 0;
int est_width = 0; /* estimated avg row width (for cache) */
int nested_cursors = 0;
ub4 i = 0;
sword status;
if (imp_sth->done_desc)
return 1; /* success, already done it */
imp_sth->done_desc = 1;
/* ora_trunc is checked at fetch time */
/* long_readlen: length for long/longraw (if >0), else 80 (ora app dflt) */
/* Ought to be for COMPAT mode only but was relaxed before LongReadLen existed */
long_readlen = (SvOK(imp_drh -> ora_long) && SvUV(imp_drh->ora_long)>0)
? SvUV(imp_drh->ora_long) : DBIc_LongReadLen(imp_sth);
/* set long_readlen for SELECT or PL/SQL with output placeholders */
imp_sth->long_readlen = long_readlen;
if (imp_sth->stmt_type != OCI_STMT_SELECT) { /* XXX DISABLED, see num_fields test below */
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_describe skipped for %s\n",
oci_stmt_type_name(imp_sth->stmt_type));
/* imp_sth memory was cleared when created so no setup required here */
return 1;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_describe %s (%s, lb %lu)...\n",
oci_stmt_type_name(imp_sth->stmt_type),
DBIc_ACTIVE(imp_sth) ? "implicit" : "EXPLICIT", (unsigned long)long_readlen);
/* We know it's a select and we've not got the description yet, so if the */
/* sth is not 'active' (executing) then we need an explicit describe. */
if ( !DBIc_ACTIVE(imp_sth) ) {
OCIStmtExecute_log_stat(imp_sth, imp_sth->svchp, imp_sth->stmhp, imp_sth->errhp,
0, 0, 0, 0, OCI_DESCRIBE_ONLY, status);
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status,
ora_sql_error(imp_sth, "OCIStmtExecute/Describe"));
if (status != OCI_SUCCESS_WITH_INFO)
return 0;
}
}
OCIAttrGet_stmhp_stat(imp_sth, &num_fields, 0, OCI_ATTR_PARAM_COUNT, status);
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status, "OCIAttrGet OCI_ATTR_PARAM_COUNT");
return 0;
}
if (num_fields == 0) {
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_describe skipped for %s (no fields returned)\n",
oci_stmt_type_name(imp_sth->stmt_type));
/* imp_sth memory was cleared when created so no setup required here */
return 1;
}
DBIc_NUM_FIELDS(imp_sth) = num_fields;
Newz(42, imp_sth->fbh, num_fields, imp_fbh_t);
/* Get number of fields and space needed for field names */
/* loop though the fields and get all the fileds and thier types to get back*/
for(i = 1; i <= num_fields; ++i) { /*start define of filed struct[i] fbh */
char *p;
ub4 atrlen;
int avg_width = 0;
imp_fbh_t *fbh = &imp_sth->fbh[i-1];
fbh->imp_sth = imp_sth;
fbh->field_num = i;
fbh->define_mode = OCI_DEFAULT;
OCIParamGet_log_stat(imp_sth, imp_sth->stmhp, OCI_HTYPE_STMT, imp_sth->errhp,
(dvoid**)&fbh->parmdp, (ub4)i, status);
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status, "OCIParamGet");
return 0;
}
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->dbtype, 0, OCI_ATTR_DATA_TYPE, status);
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->dbsize, 0, OCI_ATTR_DATA_SIZE, status);
/*may be a bug in 11 where the OCI_ATTR_DATA_SIZE my return 0 which should never happen*/
/*to fix or kludge for this I added a little code for ORA_VARCHAR2 below */
#ifdef OCI_ATTR_CHAR_USED
/* 0 means byte-length semantics, 1 means character-length semantics */
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->len_char_used, 0, OCI_ATTR_CHAR_USED, status);
/* OCI_ATTR_CHAR_SIZE: like OCI_ATTR_DATA_SIZE but measured in chars */
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->len_char_size, 0, OCI_ATTR_CHAR_SIZE, status);
#endif
fbh->csid = 0; fbh->csform = 0; /* just to be sure */
#ifdef OCI_ATTR_CHARSET_ID
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->csid, 0, OCI_ATTR_CHARSET_ID, status);
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->csform, 0, OCI_ATTR_CHARSET_FORM, status);
#endif
/* OCI_ATTR_PRECISION returns 0 for most types including some numbers */
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->prec, 0, OCI_ATTR_PRECISION, status);
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->scale, 0, OCI_ATTR_SCALE, status);
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->nullok, 0, OCI_ATTR_IS_NULL, status);
OCIAttrGet_parmdp(imp_sth, fbh->parmdp, &fbh->name, &atrlen, OCI_ATTR_NAME,status);
if (atrlen == 0) { /* long names can cause oracle to return 0 for atrlen */
char buf[99];
sprintf(buf,"field_%d_name_too_long", i);
fbh->name = &buf[0];
atrlen = strlen(fbh->name);
}
fbh->name_sv = newSVpv(fbh->name,atrlen);
fbh->name = SvPVX(fbh->name_sv);
fbh->ftype = 5; /* default: return as null terminated string */
/* TO_DO there is something wrong with the tracing below as sql_typecode_name
returns NVARCHAR2 for type 2 and ORA_NUMBER is 2 */
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Describe col #%d type=%d(%s)\n",
i,fbh->dbtype,sql_typecode_name(fbh->dbtype));
switch (fbh->dbtype) {
/* the simple types */
case ORA_VARCHAR2: /* VARCHAR2 */
if (fbh->dbsize == 0){
fbh->dbsize=4000;
}
avg_width = fbh->dbsize / 2;
/* FALLTHRU */
case ORA_CHAR: /* CHAR */
if ( CSFORM_IMPLIES_UTF8(fbh->csform) && !CS_IS_UTF8(fbh->csid) )
fbh->disize = fbh->dbsize * 4;
else
fbh->disize = fbh->dbsize;
fbh->len_char_used, fbh->len_char_size,
fbh->csid,fbh->csform,oci_csform_name(fbh->csform), fbh->disize);
if (fbh->ftype == 5) /* XXX need to handle wide chars somehow */
fbh->disize += 1; /* allow for null terminator */
/* dbsize can be zero for 'select NULL ...' */
imp_sth->t_dbsize += fbh->dbsize;
if (!avg_width)
avg_width = fbh->dbsize;
est_width += avg_width;
if (DBIc_DBISTATE(imp_sth)->debug >= 2 || dbd_verbose >= 3 )
dbd_fbh_dump(imp_sth, fbh, (int)i, 0);
}/* end define of filed struct[i] fbh*/
imp_sth->est_width = est_width;
sth_set_row_cache(h, imp_sth,
(imp_dbh->max_nested_cursors) ? 0 :nested_cursors ,
(int)num_fields, has_longs );
/* Initialise cache counters */
imp_sth->in_cache = 0;
imp_sth->eod_errno = 0;
/*rs_array_init(imp_sth);*/
/* now set up the oci call with define by pos*/
for(i=1; i <= num_fields; ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i-1];
int ftype = fbh->ftype;
/* add space for STRING null term, or VAR len prefix */
sb4 define_len = (ftype==94||ftype==95) ? fbh->disize+4 : fbh->disize;
fb_ary_t *fb_ary;
if (fbh->clbk_lob || fbh->piece_lob ){/*init the cb_abuf with this call*/
fbh->fb_ary = fb_ary_cb_alloc(imp_sth->piece_size,define_len, imp_sth->rs_array_size);
} else {
fbh->fb_ary = fb_ary_alloc(define_len, imp_sth->rs_array_size);
}
fb_ary = fbh->fb_ary;
if (fbh->ftype == SQLT_BIN) {
define_len++;
/*add one extra byte incase the size of the lob is equal to the define_len*/
}
if (fbh->ftype == ORA_RSET) { /* RSET */
OCIHandleAlloc_ok(imp_sth, imp_sth->envhp,
(dvoid*)&((OCIStmt **)fb_ary->abuf)[0],
OCI_HTYPE_STMT, status);
}
OCIDefineByPos_log_stat(imp_sth, imp_sth->stmhp,
&fbh->defnp,
imp_sth->errhp,
(ub4) i,
(fbh->desc_h) ? (dvoid*)&fbh->desc_h : fbh->clbk_lob ? (dvoid *) 0: fbh->piece_lob ? (dvoid *) 0:(dvoid*)fb_ary->abuf,
(fbh->desc_h) ? 0 : define_len,
(ub2)fbh->ftype,
fb_ary->aindp,
(ftype==94||ftype==95) ? NULL : fb_ary->arlen,
fb_ary->arcode,
fbh->define_mode,
status);
if (fbh->clbk_lob){
/* use a dynamic callback for persistent binary and char lobs*/
OCIDefineDynamic_log_stat(imp_sth, fbh->defnp,imp_sth->errhp,(dvoid *) fbh,status);
}
if (fbh->ftype == 108) { /* Embedded object bind it differently*/
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ){
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"Field #%d is a object or colection of some sort. "
"Using OCIDefineObject and or OCIObjectPin \n",i);
}
Newz(1, fbh->obj, 1, fbh_obj_t);
fbh->obj->typecode=fbh->dbtype;
if (!describe_obj(h,imp_sth,fbh->parmdp,fbh->obj,0)){
++num_errors;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ){
dump_struct(imp_sth,fbh->obj,0);
}
OCIDefineObject_log_stat(imp_sth,fbh->defnp,imp_sth->errhp,fbh->obj->tdo,(dvoid**)&fbh->obj->obj_value,(dvoid**)&fbh->obj->obj_ind,status);
if (status != OCI_SUCCESS) {
oci_error(h,imp_sth->errhp, status, "OCIDefineObject");
++num_errors;
}
}
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status, "OCIDefineByPos");
++num_errors;
}
#ifdef OCI_ATTR_CHARSET_FORM
if ( (fbh->dbtype == 1) && fbh->csform ) {
/* csform may be 0 when talking to Oracle 8.0 database*/
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" calling OCIAttrSet OCI_ATTR_CHARSET_FORM with csform=%d (%s)\n",
fbh->csform,oci_csform_name(fbh->csform) );
OCIAttrSet_log_stat(imp_sth, fbh->defnp, (ub4) OCI_HTYPE_DEFINE, (dvoid *) &fbh->csform,
(ub4) 0, (ub4) OCI_ATTR_CHARSET_FORM, imp_sth->errhp, status );
if (status != OCI_SUCCESS) {
oci_error(h, imp_sth->errhp, status, "OCIAttrSet OCI_ATTR_CHARSET_FORM");
++num_errors;
}
}
#endif /* OCI_ATTR_CHARSET_FORM */
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_describe'd %d columns (row bytes: %d max, %d est avg, cache: %d)\n",
(int)num_fields, imp_sth->t_dbsize, imp_sth->est_width,
imp_sth->prefetch_rows);
return (num_errors>0) ? 0 : 1;
}
AV *
dbd_st_fetch(SV *sth, imp_sth_t *imp_sth){
dTHX;
D_imp_xxh(sth);
sword status;
D_imp_dbh_from_sth;
int num_fields = DBIc_NUM_FIELDS(imp_sth);
int ChopBlanks;
int err;
int i;
AV *av;
/* Check that execute() was executed sucessfully. This also implies */
/* that dbd_describe() executed sucessfuly so the memory buffers */
/* are allocated and bound. */
if ( !DBIc_ACTIVE(imp_sth) ) {
oci_error(sth, NULL, OCI_ERROR, imp_sth->nested_cursor ?
"nested cursor is defunct (parent row is no longer current)" :
"no statement executing (perhaps you need to call execute first)");
return Nullav;
}
for(i=0; i < num_fields; ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i];
if (fbh->fetch_cleanup)
fbh->fetch_cleanup(sth, fbh);
}
if (ora_fetchtest && DBIc_ROW_COUNT(imp_sth)>0) {
--ora_fetchtest; /* trick for testing performance */
status = OCI_SUCCESS;
}
else {
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 ){
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_st_fetch %d fields...\n", DBIc_NUM_FIELDS(imp_sth));
}
if (imp_sth->fetch_orient != OCI_DEFAULT) {
if (imp_sth->exe_mode!=OCI_STMT_SCROLLABLE_READONLY)
croak ("attempt to use a scrollable cursor without first setting ora_exe_mode to OCI_STMT_SCROLLABLE_READONLY\n") ;
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Scrolling Fetch, position before fetch=%d, "
"Orientation = %s , Fetchoffset =%d\n",
imp_sth->fetch_position, oci_fetch_options(imp_sth->fetch_orient),
imp_sth->fetch_offset);
OCIStmtFetch_log_stat(imp_sth, imp_sth->stmhp, imp_sth->errhp,1, imp_sth->fetch_orient,imp_sth->fetch_offset, status);
/*this will work without a round trip so might as well open it up for all statments handles*/
/* default and OCI_FETCH_NEXT are the same so this avoids miscaluation on the next value*/
if (status==OCI_NO_DATA){
return Nullav;
}
OCIAttrGet_stmhp_stat(imp_sth, &imp_sth->fetch_position, 0, OCI_ATTR_CURRENT_POSITION, status);
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" Scrolling Fetch, postion after fetch=%d\n",
imp_sth->fetch_position);
}
else {
if (imp_sth->row_cache_off){ /*Do not use array fetch or local cache */
OCIStmtFetch_log_stat(imp_sth, imp_sth->stmhp, imp_sth->errhp,1,(ub2)OCI_FETCH_NEXT, OCI_DEFAULT, status);
imp_sth->rs_fetch_count++;
imp_sth->rs_array_idx=0;
}
else { /*Array Fetch the New Normal Super speedy and very nice*/
imp_sth->rs_array_idx++;
if (imp_sth->rs_array_num_rows<=imp_sth->rs_array_idx && (imp_sth->rs_array_status==OCI_SUCCESS || imp_sth->rs_array_status==OCI_SUCCESS_WITH_INFO)) {
/* PerlIO_printf(DBIc_LOGPIO(imp_sth), " dbd_st_fetch fields...b\n");*/
OCIStmtFetch_log_stat(imp_sth, imp_sth->stmhp,imp_sth->errhp,imp_sth->rs_array_size,(ub2)OCI_FETCH_NEXT,OCI_DEFAULT,status);
imp_sth->rs_array_status=status;
imp_sth->rs_fetch_count++;
if (oci_warn && (imp_sth->rs_array_status == OCI_SUCCESS_WITH_INFO)) {
oci_error(sth, imp_sth->errhp, status, "OCIStmtFetch");
}
OCIAttrGet_stmhp_stat(imp_sth, &imp_sth->rs_array_num_rows,0,OCI_ATTR_ROWS_FETCHED, status);
imp_sth->rs_array_idx=0;
imp_dbh->RowsInCache =imp_sth->rs_array_size;
imp_sth->RowsInCache =imp_sth->rs_array_size;
if (DBIc_DBISTATE(imp_sth)->debug >= 4 || dbd_verbose >= 4 || oci_warn)
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"...Fetched %d rows\n",imp_sth->rs_array_num_rows);
}
imp_dbh->RowsInCache--;
imp_sth->RowsInCache--;
if (imp_sth->rs_array_num_rows>imp_sth->rs_array_idx) /* set status to success if rows in cache */
status=OCI_SUCCESS;
else
status=imp_sth->rs_array_status;
}
}
}
if (status != OCI_SUCCESS && status !=OCI_NEED_DATA) {
ora_fetchtest = 0;
if (status == OCI_NO_DATA) {
dTHR; /* for DBIc_ACTIVE_off */
DBIc_ACTIVE_off(imp_sth); /* eg finish */
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 || oci_warn)
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_st_fetch no-more-data, fetch count=%d\n",
imp_sth->rs_fetch_count-1);
return Nullav;
}
if (status != OCI_SUCCESS_WITH_INFO) {
dTHR; /* for DBIc_ACTIVE_off */
DBIc_ACTIVE_off(imp_sth); /* eg finish */
oci_error(sth, imp_sth->errhp, status, "OCIStmtFetch");
return Nullav;
}
if (oci_warn && (status == OCI_SUCCESS_WITH_INFO)) {
oci_error(sth, imp_sth->errhp, status, "OCIStmtFetch");
}
/* for OCI_SUCCESS_WITH_INFO we fall through and let the */
/* per-field rcode value be dealt with as we fetch the data */
}
av = DBIc_DBISTATE(imp_sth)->get_fbav(imp_sth);
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 ) {
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" dbd_st_fetched %d fields with status of %d(%s)\n",
num_fields,status, oci_status_name(status));
}
else { /* See odefin rcode arg description in OCI docs */
char buf[200];
char *hint = "";
/* These may get more case-by-case treatment eventually. */
if (rc == 1406) { /* field truncated (see above) */
if (!fbh->fetch_func) {
/* Copy the truncated value anyway, it may be of use, */
/* but it'll only be accessible via prior bind_column() */
sv_setpvn(sv, (char *)row_data,fb_ary->arlen[imp_sth->rs_array_idx]);
if ((CSFORM_IMPLIES_UTF8(fbh->csform)) && (fbh->ftype != SQLT_BIN)){
SvUTF8_on(sv);
}
}
if (ora_dbtype_is_long(fbh->dbtype)){ /* double check */
hint = ", LongReadLen too small and/or LongTruncOk not set";
}
}
else { /* set field that caused error to undef */
sv_set_undef(sv);
}
++err; /* 'fail' this fetch but continue getting fields */
/* Some should probably be treated as warnings but */
/* for now we just treat them all as errors */
sprintf(buf,"ORA-%05d error on field %d of %d, ora_type %d%s",rc, i+1, num_fields, fbh->dbtype, hint);
oci_error(sth, imp_sth->errhp, OCI_ERROR, buf);
}
if (DBIc_DBISTATE(imp_sth)->debug >= 5 || dbd_verbose >= 5 ){
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
"\n %p (field=%d): %s\n", av, i,neatsvpv(sv,10));
}
}
return (err) ? Nullav : av;
}
ub4
ora_parse_uid(imp_dbh_t *imp_dbh, char **uidp, char **pwdp)
{
dTHX;
sword status;
/* OCI 8 does not seem to allow uid to be "name/pass" :-( */
/* so we have to split it up ourselves */
if (strlen(*pwdp)==0 && strchr(*uidp,'/')) {
SV *tmpsv = sv_2mortal(newSVpv(*uidp,0));
*uidp = SvPVX(tmpsv);
*pwdp = strchr(*uidp, '/');
*(*pwdp)++ = '\0';
/* XXX look for '@', e.g. "u/p@d" and "u@d" and maybe "@d"? */
}
if (**uidp == '\0' && **pwdp == '\0') {
return OCI_CRED_EXT;
}
#ifdef ORA_OCI_112
if (!imp_dbh->using_drcp) {
#endif
OCIAttrSet_log_stat(imp_dbh, imp_dbh->seshp, OCI_HTYPE_SESSION,
*uidp, strlen(*uidp),
(ub4) OCI_ATTR_USERNAME, imp_dbh->errhp, status);
OCIAttrSet_log_stat(imp_dbh, imp_dbh->seshp, OCI_HTYPE_SESSION,
(strlen(*pwdp)) ? *pwdp : NULL, strlen(*pwdp),
(ub4) OCI_ATTR_PASSWORD, imp_dbh->errhp, status);
#ifdef ORA_OCI_112
}
#endif
return OCI_CRED_RDBMS;
}
int
ora_db_reauthenticate(SV *dbh, imp_dbh_t *imp_dbh, char *uid, char *pwd)
{
dTHX;
sword status;
#ifdef ORA_OCI_112
if (imp_dbh->using_drcp) {
return 0;
}
#endif
/* XXX should possibly create new session before ending the old so */
/* that if the new one can't be created, the old will still work. */
OCISessionEnd_log_stat(imp_dbh, imp_dbh->svchp, imp_dbh->errhp,
imp_dbh->seshp, OCI_DEFAULT, status); /* XXX check status here?*/
OCISessionBegin_log_stat(imp_dbh, imp_dbh->svchp, imp_dbh->errhp, imp_dbh->seshp,
ora_parse_uid(imp_dbh, &uid, &pwd), (ub4) OCI_DEFAULT, status);
if (status != OCI_SUCCESS) {
oci_error(dbh, imp_dbh->errhp, status, "OCISessionBegin");
return 0;
}
return 1;
}
#ifdef not_used_curently
static char *
rowid2hex(OCIRowid *rowid)
{
int i;
SV *sv = sv_2mortal(newSVpv("",0));
for (i = 0; i < OCI_ROWID_LEN; i++) {
char buf[6];
sprintf(buf, "%02X ", (int)(((ub1*)rowid)[i]));
sv_catpv(sv, buf);
}
return SvPVX(sv);
}
#endif
static void *
alloc_via_sv(STRLEN len, SV **svp, int mortal)
{
dTHX;
SV *sv = newSVpv("",0);
sv_grow(sv, len+1);
memset(SvPVX(sv), 0, len);
if (mortal)
sv_2mortal(sv);
if (svp)
*svp = sv;
return SvPVX(sv);
}
char *
find_ident_after(char *src, char *after, STRLEN *len, int copy)
{
int seen_key = 0;
char *orig = src;
char *p;
while(*src){
if (*src == '\'') {
char delim = *src;
while(*src && *src != delim) ++src;
}
else if (*src == '-' && src[1] == '-') {
while(*src && *src != '\n') ++src;
}
else if (*src == '/' && src[1] == '*') {
while(*src && !(*src == '*' && src[1]=='/')) ++src;
}
struct lob_refetch_st {
OCIStmt *stmthp;
OCIBind *bindhp;
OCIRowid *rowid;
OCIParam *parmdp_tmp;
OCIParam *parmdp_lob;
int num_fields;
SV *fbh_ary_sv;
imp_fbh_t *fbh_ary;
};
static int
init_lob_refetch(SV *sth, imp_sth_t *imp_sth)
{
dTHX;
SV *sv;
SV *sql_select;
HV *lob_cols_hv = NULL;
sword status;
OCIError *errhp = imp_sth->errhp;
OCIParam *parmhp = NULL, *collisthd = NULL, *colhd = NULL;
ub2 numcols = 0;
imp_fbh_t *fbh;
int unmatched_params;
I32 i,j;
char *p;
lob_refetch_t *lr = NULL;
STRLEN tablename_len;
char *tablename;
char new_tablename[100];
switch (imp_sth->stmt_type) {
case OCI_STMT_UPDATE:
tablename = find_ident_after(imp_sth->statement,
"update", &tablename_len, 1);
break;
case OCI_STMT_INSERT:
tablename = find_ident_after(imp_sth->statement,
"into", &tablename_len, 1);
break;
default:
return oci_error(sth, errhp, OCI_ERROR,
"LOB refetch attempted for unsupported statement type (see also ora_auto_lob attribute)");
}
if (!tablename)
return oci_error(sth, errhp, OCI_ERROR,
"Unable to parse table name for LOB refetch");
if (!imp_sth->dschp){
OCIHandleAlloc_ok(imp_sth, imp_sth->envhp, &imp_sth->dschp, OCI_HTYPE_DESCRIBE, status);
if (status != OCI_SUCCESS) {
oci_error(sth,imp_sth->errhp, status, "OCIHandleAlloc");
}
}
OCIDescribeAny_log_stat(imp_sth, imp_sth->svchp, errhp, tablename, strlen(tablename),
(ub1)OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_SYN, imp_sth->dschp, status);
if (status == OCI_SUCCESS) { /* There is a synonym, get the schema */
char *syn_schema=NULL;
char syn_name[100];
ub4 tn_len = 0, syn_schema_len = 0;
strncpy(syn_name,tablename,strlen(tablename));
/* Put the synonym name here for later user */
OCIAttrGet_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE,
&parmhp, 0, OCI_ATTR_PARAM, errhp, status);
OCIAttrGet_log_stat(imp_sth, parmhp, OCI_DTYPE_PARAM,
&syn_schema, &syn_schema_len, OCI_ATTR_SCHEMA_NAME, errhp, status);
OCIAttrGet_log_stat(imp_sth, parmhp, OCI_DTYPE_PARAM,
&tablename, &tn_len, OCI_ATTR_NAME, errhp, status);
strncpy(new_tablename,syn_schema,syn_schema_len);
new_tablename[syn_schema_len+1] = '\0';
new_tablename[syn_schema_len]='.';
strncat(new_tablename, tablename,tn_len);
tablename=new_tablename;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch using a synonym named=%s for %s \n",
syn_name,tablename);
}
OCIDescribeAny_log_stat(imp_sth, imp_sth->svchp, errhp, tablename, strlen(tablename),
(ub1)OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_TABLE, imp_sth->dschp, status);
if (status != OCI_SUCCESS) {
/* XXX this OCI_PTYPE_TABLE->OCI_PTYPE_VIEW fallback should actually be */
/* a loop that includes synonyms etc */
OCIDescribeAny_log_stat(imp_sth, imp_sth->svchp, errhp, tablename, strlen(tablename),
(ub1)OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_VIEW, imp_sth->dschp, status);
if (status != OCI_SUCCESS) {
OCIHandleFree_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE, status);
return oci_error(sth, errhp, status, "OCIDescribeAny(view)/LOB refetch");
}
}
OCIAttrGet_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE,
&parmhp, 0, OCI_ATTR_PARAM, errhp, status);
if (!status ) {
OCIAttrGet_log_stat(imp_sth, parmhp, OCI_DTYPE_PARAM,
&numcols, 0, OCI_ATTR_NUM_COLS, errhp, status);
}
if (!status ) {
OCIAttrGet_log_stat(imp_sth, parmhp, OCI_DTYPE_PARAM,
&collisthd, 0, OCI_ATTR_LIST_COLUMNS, errhp, status);
}
if (status != OCI_SUCCESS) {
OCIHandleFree_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE, status);
return oci_error(sth, errhp, status, "OCIDescribeAny/OCIAttrGet/LOB refetch");
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch from table %s, %d columns:\n",
tablename, numcols);
for (i = 1; i <= (long)numcols; i++) {
ub2 col_dbtype;
char *col_name;
ub4 col_name_len;
OCIParamGet_log_stat(imp_sth, collisthd, OCI_DTYPE_PARAM, errhp, (dvoid**)&colhd, i, status);
if (status)
break;
OCIAttrGet_log_stat(imp_sth, colhd, OCI_DTYPE_PARAM, &col_dbtype, 0,
OCI_ATTR_DATA_TYPE, errhp, status);
if (status)
break;
OCIAttrGet_log_stat(imp_sth, colhd, OCI_DTYPE_PARAM, &col_name, &col_name_len,
OCI_ATTR_NAME, errhp, status);
if (status)
break;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch table col %d: '%.*s' otype %d\n",
(int)i, (int)col_name_len,col_name, col_dbtype);
if (col_dbtype != SQLT_CLOB && col_dbtype != SQLT_BLOB)
continue;
if (!lob_cols_hv)
lob_cols_hv = newHV();
sv = newSViv(col_dbtype);
(void)sv_setpvn(sv, col_name, col_name_len);
if (CSFORM_IMPLIES_UTF8(SQLCS_IMPLICIT))
SvUTF8_on(sv);
(void)SvIOK_on(sv); /* "what a wonderful hack!" */
(void)hv_store(lob_cols_hv, col_name,col_name_len, sv,0);
OCIDescriptorFree_log(imp_sth, colhd, OCI_DTYPE_PARAM);
colhd = NULL;
}
if (colhd)
OCIDescriptorFree_log(imp_sth, colhd, OCI_DTYPE_PARAM);
if (status != OCI_SUCCESS) {
oci_error(sth, errhp, status,
"OCIDescribeAny/OCIParamGet/OCIAttrGet/LOB refetch");
OCIHandleFree_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE, status);
return 0;
}
if (!lob_cols_hv)
return oci_error(sth, errhp, OCI_ERROR,
"LOB refetch failed, no lobs in table");
/* our bind params are in %imp_sth->all_params_hv
our table cols are in %lob_cols_hv
we now iterate through our bind params
and allocate them to the appropriate table columns
*/
Newz(1, lr, 1, lob_refetch_t);
unmatched_params = 0;
lr->num_fields = 0;
lr->fbh_ary = (imp_fbh_t*)alloc_via_sv(sizeof(imp_fbh_t) * HvKEYS(lob_cols_hv)+1,
&lr->fbh_ary_sv, 0);
sql_select = sv_2mortal(newSVpv("select ",0));
hv_iterinit(imp_sth->all_params_hv);
while( (sv = hv_iternextsv(imp_sth->all_params_hv, &p, &i)) != NULL ) {
int matched = 0;
phs_t *phs = (phs_t*)(void*)SvPVX(sv);
if (sv == &PL_sv_undef || !phs)
croak("panic: unbound params");
if (phs->ftype != SQLT_CLOB && phs->ftype != SQLT_BLOB)
continue;
hv_iterinit(lob_cols_hv);
while( (sv = hv_iternextsv(lob_cols_hv, &p, &j)) != NULL ) {
char sql_field[200];
if (phs->ora_field) { /* must match this phs by field name */
char *ora_field_name = SvPV(phs->ora_field,PL_na);
if (SvCUR(phs->ora_field) != SvCUR(sv)
|| ibcmp(ora_field_name, SvPV(sv,PL_na), (I32)SvCUR(sv) ) )
continue;
}
else { /* basic dumb match by type */
if (phs->ftype != SvIV(sv)){
continue;
}
else { /* got a type match - check it's safe */
SV *sv_other;
char *p_other;
/* would any other lob field match this type? */
while( (sv_other = hv_iternextsv(lob_cols_hv, &p_other, &i)) != NULL ) {
if (phs->ftype != SvIV(sv_other))
continue;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" both %s and %s have type %d - ambiguous\n",
neatsvpv(sv,0), neatsvpv(sv_other,0),
(int)SvIV(sv_other));
Safefree(lr);
sv_free((SV*)lob_cols_hv);
return oci_error(sth, errhp, OCI_ERROR,
"Need bind_param(..., { ora_field=>... }) attribute to identify table LOB field names");
}
}
}
matched = 1;
sprintf(sql_field, "%s%s \"%s\"",
(SvCUR(sql_select)>7)?", ":"", p, &phs->name[1]);
sv_catpv(sql_select, sql_field);
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch %s param: otype %d, matched field '%s' %s(%s)\n",
phs->name, phs->ftype, p,
(phs->ora_field) ? "by name " : "by type ", sql_field);
(void)hv_delete(lob_cols_hv, p, i, G_DISCARD);
fbh = &lr->fbh_ary[lr->num_fields++];
fbh->name = phs->name;
fbh->ftype = phs->ftype;
fbh->dbtype = phs->ftype;
fbh->disize = 99;
fbh->desc_t = OCI_DTYPE_LOB;
OCIDescriptorAlloc_ok(imp_sth, imp_sth->envhp, &fbh->desc_h, fbh->desc_t);
break; /* we're done with this placeholder now */
}
if (!matched) {
++unmatched_params;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch %s param: otype %d, UNMATCHED\n",
phs->name, phs->ftype);
}
}
sv_free((SV*)lob_cols_hv);
if (unmatched_params) {
Safefree(lr);
return oci_error(sth, errhp, OCI_ERROR,
"Can't match some parameters to LOB fields in the table, check type and name");
}
sv_catpv(sql_select, " from ");
sv_catpv(sql_select, tablename);
sv_catpv(sql_select, " where rowid = :rid for update"); /* get row with lock */
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch sql: %s\n", SvPVX(sql_select));
lr->stmthp = NULL;
lr->bindhp = NULL;
lr->rowid = NULL;
lr->parmdp_tmp = NULL;
lr->parmdp_lob = NULL;
OCIHandleAlloc_ok(imp_sth, imp_sth->envhp, &lr->stmthp, OCI_HTYPE_STMT, status);
OCIStmtPrepare_log_stat(imp_sth, lr->stmthp, errhp,
(text*)SvPVX(sql_select), SvCUR(sql_select), OCI_NTV_SYNTAX,
OCI_DEFAULT, status);
if (status != OCI_SUCCESS) {
OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT);
Safefree(lr);
return oci_error(sth, errhp, status, "OCIStmtPrepare/LOB refetch");
}
/* bind the rowid input */
OCIDescriptorAlloc_ok(imp_sth, imp_sth->envhp, &lr->rowid, OCI_DTYPE_ROWID);
OCIBindByName_log_stat(imp_sth, lr->stmthp, &lr->bindhp, errhp, (text*)":rid", 4,
&lr->rowid, sizeof(OCIRowid*), SQLT_RDD, 0,0,0,0,0, OCI_DEFAULT, status);
if (status != OCI_SUCCESS) {
OCIDescriptorFree_log(imp_sth, lr->rowid, OCI_DTYPE_ROWID);
OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT);
Safefree(lr);
return oci_error(sth, errhp, status, "OCIBindByPos/LOB refetch");
}
/* define the output fields */
for(i=0; i < lr->num_fields; ++i) {
OCIDefine *defnp = NULL;
imp_fbh_t *fbh = &lr->fbh_ary[i];
phs_t *phs;
SV **phs_svp = hv_fetch(imp_sth->all_params_hv, fbh->name,strlen(fbh->name), 0);
if (!phs_svp)
croak("panic: LOB refetch for '%s' param (%ld) - name not found",fbh->name,(unsigned long)i+1);
phs = (phs_t*)(void*)SvPVX(*phs_svp);
fbh->special = phs;
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch %d for '%s' param: ftype %d setup\n",
(int)i+1,fbh->name, fbh->dbtype);
fbh->fb_ary = fb_ary_alloc(fbh->disize, 1);
OCIDefineByPos_log_stat(imp_sth, lr->stmthp, &defnp, errhp, (ub4)i+1,
&fbh->desc_h, -1, (ub2)fbh->ftype,
fbh->fb_ary->aindp, 0, fbh->fb_ary->arcode, OCI_DEFAULT, status);
if (status != OCI_SUCCESS) {
OCIDescriptorFree_log(imp_sth, lr->rowid, OCI_DTYPE_ROWID);
OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT);
Safefree(lr);
fb_ary_free(fbh->fb_ary);
fbh->fb_ary = NULL;
return oci_error(sth, errhp, status, "OCIDefineByPos/LOB refetch");
}
}
OCIHandleFree_log_stat(imp_sth, imp_sth->dschp, OCI_HTYPE_DESCRIBE, status);
imp_sth->lob_refetch = lr; /* structure copy */
return 1;
}
int
post_execute_lobs(SV *sth, imp_sth_t *imp_sth, ub4 row_count) /* XXX leaks handles on error */
{
/* To insert a new LOB transparently (without using 'INSERT . RETURNING .') */
/* we have to insert an empty LobLocator and then fetch it back from the */
/* server before we can call OCILobWrite on it! This function handles that. */
dTHX;
sword status;
int i;
OCIError *errhp = imp_sth->errhp;
lob_refetch_t *lr;
D_imp_dbh_from_sth;
SV *dbh = (SV*)DBIc_MY_H(imp_dbh);
if (!imp_sth->auto_lob)
return 1; /* application doesn't want magical lob handling */
if (imp_sth->stmt_type == OCI_STMT_BEGIN || imp_sth->stmt_type == OCI_STMT_DECLARE){
/* PL/SQL is handled by lob_phs_ora_free_templobpost_execute */
if (imp_sth->has_lobs) { /*get rid of OCILob Temporary used in non inout bind*/
SV *phs_svp;
I32 i;
char *p;
hv_iterinit(imp_sth->all_params_hv);
while( (phs_svp = hv_iternextsv(imp_sth->all_params_hv, &p, &i)) != NULL ) {
phs_t *phs = (phs_t*)(void*)SvPVX(phs_svp);
if (phs->desc_h && !phs->is_inout){
OCILobFreeTemporary_log_stat(imp_sth, imp_sth->svchp, imp_sth->errhp, phs->desc_h, status);
/* boolean lobEmpty=1;*/
/* OCIAttrSet_log_stat(phs->desc_h, phs->desc_t,&lobEmpty, 0, OCI_ATTR_LOBEMPTY, imp_sth->errhp, status);*/
/* OCIHandleFree_log_stat(phs->desc_h, phs->desc_t, status);*/
}
/*this seem to cause an error later on so I just got rid of it for Now does */
/* not seem to kill anything */
}
}
return 1;
}
if (row_count == 0)
return 1; /* nothing to do */
if (row_count > 1)
return oci_error(sth, errhp, OCI_ERROR, "LOB refetch attempted for multiple rows");
if (!imp_sth->lob_refetch) {
if (!init_lob_refetch(sth, imp_sth))
return 0; /* init_lob_refetch already called oci_error */
}
lr = imp_sth->lob_refetch;
OCIAttrGet_stmhp_stat(imp_sth, lr->rowid, 0, OCI_ATTR_ROWID,status);
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status, "OCIAttrGet OCI_ATTR_ROWID /LOB refetch");
OCIStmtExecute_log_stat(imp_sth, imp_sth->svchp, lr->stmthp, errhp,1, 0, NULL, NULL, OCI_DEFAULT, status); /* execute and fetch */
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status,
ora_sql_error(imp_sth,"OCIStmtExecute/LOB refetch"));
for(i=0; i < lr->num_fields; ++i) {
imp_fbh_t *fbh = &lr->fbh_ary[i];
int rc = fbh->fb_ary->arcode[0];
phs_t *phs = (phs_t*)fbh->special;
ub4 amtp;
(void)SvUPGRADE(phs->sv, SVt_PV);
amtp = SvCUR(phs->sv); /* XXX UTF8? */
if (rc == 1405) { /* NULL - return undef */
sv_set_undef(phs->sv);
status = OCI_SUCCESS;
}
else if (amtp > 0) { /* since amtp==0 & OCI_ONE_PIECE fail (OCI 8.0.4) */
if( ! fbh->csid ) {
ub1 csform = SQLCS_IMPLICIT;
ub2 csid = 0;
OCILobCharSetForm_log_stat(imp_sth,
imp_sth->envhp,
errhp,
(OCILobLocator*)fbh->desc_h,
&csform,
status );
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status, "OCILobCharSetForm");
#ifdef OCI_ATTR_CHARSET_ID
/* Effectively only used so AL32UTF8 works properly */
OCILobCharSetId_log_stat(imp_sth,
imp_sth->envhp,
errhp,
(OCILobLocator*)fbh->desc_h,
&csid,
status );
if (status != OCI_SUCCESS)
return oci_error(sth, errhp, status, "OCILobCharSetId");
#endif /* OCI_ATTR_CHARSET_ID */
/* if data is utf8 but charset isn't then switch to utf8 csid */
csid = (SvUTF8(phs->sv) && !CS_IS_UTF8(csid)) ? utf8_csid : CSFORM_IMPLIED_CSID(csform);
fbh->csid = csid;
fbh->csform = csform;
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" calling OCILobWrite fbh->csid=%d fbh->csform=%d amtp=%d\n",
fbh->csid, fbh->csform, amtp );
OCILobWrite_log_stat(imp_sth, imp_sth->svchp, errhp,
(OCILobLocator*)fbh->desc_h, &amtp, 1, SvPVX(phs->sv), amtp, OCI_ONE_PIECE,
0,0, fbh->csid ,fbh->csform, status);
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status, "OCILobWrite in post_execute_lobs");
}
} else { /* amtp==0 so truncate LOB to zero length */
OCILobTrim_log_stat(imp_sth, imp_sth->svchp, errhp, (OCILobLocator*)fbh->desc_h, 0, status);
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status, "OCILobTrim in post_execute_lobs");
}
}
if (DBIc_DBISTATE(imp_sth)->debug >= 3 || dbd_verbose >= 3 )
PerlIO_printf(
DBIc_LOGPIO(imp_sth),
" lob refetch %d for '%s' param: ftype %d, len %ld: %s %s\n",
i+1,fbh->name, fbh->dbtype, ul_t(amtp),
(rc==1405 ? "NULL" : (amtp > 0) ? "LobWrite" : "LobTrim"), oci_status_name(status));
if (status != OCI_SUCCESS) {
return oci_error(sth, errhp, status, "OCILobTrim/OCILobWrite/LOB refetch");
}
}
if (DBIc_has(imp_dbh,DBIcf_AutoCommit))
dbd_db_commit(dbh, imp_dbh);
return 1;
}
void
ora_free_lob_refetch(SV *sth, imp_sth_t *imp_sth)
{
dTHX;
lob_refetch_t *lr = imp_sth->lob_refetch;
int i;
sword status;
if (lr->rowid)
OCIDescriptorFree_log(imp_sth, lr->rowid, OCI_DTYPE_ROWID);
OCIHandleFree_log_stat(imp_sth, lr->stmthp, OCI_HTYPE_STMT, status);
if (status != OCI_SUCCESS)
oci_error(sth, imp_sth->errhp, status, "ora_free_lob_refetch/OCIHandleFree");
for(i=0; i < lr->num_fields; ++i) {
imp_fbh_t *fbh = &lr->fbh_ary[i];
ora_free_fbh_contents(sth, fbh);
}
sv_free(lr->fbh_ary_sv);
Safefree(imp_sth->lob_refetch);
imp_sth->lob_refetch = NULL;
}
ub4
ora_db_version(SV *dbh, imp_dbh_t *imp_dbh)
{
dTHX;
sword status;
text buf[2];
ub4 vernum;
if( imp_dbh->server_version > 0 ) {
return imp_dbh->server_version;
}
/* XXX should possibly create new session before ending the old so */
/* that if the new one can't be created, the old will still work. */
OCIServerRelease_log_stat(imp_dbh, imp_dbh->svchp, imp_dbh->errhp, buf, 2,OCI_HTYPE_SVCCTX, &vernum , status);
if (status != OCI_SUCCESS) {
oci_error(dbh, imp_dbh->errhp, status, "OCISessionServerRelease");
return 0;
}
imp_dbh->server_version = vernum;
return vernum;
}
( run in 1.432 second using v1.01-cache-2.11-cpan-5837b0d9d2c )