Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the DBD-Oracle CPAN distribution.

Report information
The Basics
Id: 6560
Status: resolved
Priority: 0/
Queue: DBD-Oracle

People
Owner: Nobody in particular
Requestors: bai [...] dreamarts.co.jp
Cc:
AdminCc:

Bug Information
Severity: Critical
Broken in: 1.15
Fixed in: (no value)



Subject: BLOB/CLOB memory leak patch
Package: DBD-Oracle-1.15 Environment: perl-5.6.0, i386-linux 2.4.7-2.24ml, Oracle-8.1.7 Description: When using Oracle BLOB from within mod_perl, we found the size of httpd process increasing. If you changes BLOB to LONGRAW, there will be no memory increasing. Our module using DBI-1.41 and DBD-Oracle-1.15, and just "simple LOB" were used. Reproduce Steps: 1) Create a table on Oracle: CREATE TABLE lob (id INTEGER PRIMARY KEY, object_data BLOB) 2) Using following trivial snippet to insert binary as BLOB for 1000 times: sub insert($$$) { my ($dbh, $id, $object) = @_; my $sql = "INSERT INTO lob(id, object_data) VALUES (?, ?)"; my $sth = $dbh->prepare($sql); $sth->bind_param(1, $id); $sth->bind_param(2, $object, {ora_type => ORA_BLOB}); my $rv = $sth->execute(); $sth->finish(); } A binary file about 2KB was used in our test. 3) You can observe at a "top" console that the memory usage increasing at about 4096 bytes per insert call. Patch: It spent 2 days to dig it, all leaks are in oci8.c, inside function init_lob_refetch() and ora_free_lob_refetch(), leaks happened in XSUB and OCI calls. By the experiences on XSUB and OCI, it is possible for me to contribute a "LOB leak" patch. The patch has been tested on both "simple LOB" and "Using Locator" pattern, both "INSERT" and "UPDATE" operations. For "Using Locator" INSERT/UPDATE test examples, just see author Tim Bunce's detailed document: http://search.cpan.org/~timb/DBD-Oracle-1.15/Oracle.pm, section "Handling LOBs". Dongqiang Bai 2004-06-09
diff -Naur DBD-Oracle-1.15/oci8.c DBD-Oracle-1.15-patch/oci8.c --- DBD-Oracle-1.15/oci8.c Sat Jan 10 09:00:34 2004 +++ DBD-Oracle-1.15-patch/oci8.c Wed Jun 9 10:25:41 2004 @@ -1491,7 +1491,7 @@ ++src; *len = src - start; if (copy) { - p = alloc_via_sv(*len, 0, 1); + p = (char *)malloc((*len) + 1); strncpy(p, start, *len); p[*len] = '\0'; return p; @@ -1581,6 +1581,7 @@ strcpy(new_tablename, syn_schema); strcat(new_tablename, "."); strcat(new_tablename, syn_name); + free(tablename); tablename=new_tablename; if (DBIS->debug >= 3) PerlIO_printf(DBILOGFP, " lob refetch synonym, schema=%s, name=%s, new tablename=%s\n", syn_schema, syn_name, tablename); @@ -1595,6 +1596,8 @@ (ub1)OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_VIEW, dschp, status); if (status != OCI_SUCCESS) { OCIHandleFree_log_stat(dschp, OCI_HTYPE_DESCRIBE, status); + if (tablename != new_tablename) + free(tablename); return oci_error(sth, errhp, status, "OCIDescribeAny(view)/LOB refetch"); } } @@ -1611,6 +1614,8 @@ } if (status != OCI_SUCCESS) { OCIHandleFree_log_stat(dschp, OCI_HTYPE_DESCRIBE, status); + if (tablename != new_tablename) + free(tablename); return oci_error(sth, errhp, status, "OCIDescribeAny/OCIAttrGet/LOB refetch"); } if (DBIS->debug >= 3) @@ -1627,35 +1632,47 @@ break; OCIAttrGet_log_stat(colhd, OCI_DTYPE_PARAM, &col_dbtype, 0, OCI_ATTR_DATA_TYPE, errhp, status); - if (status) - break; + if (status) { + OCIDescriptorFree(colhd, OCI_DTYPE_PARAM); + break; + } OCIAttrGet_log_stat(colhd, OCI_DTYPE_PARAM, &col_name, &col_name_len, OCI_ATTR_NAME, errhp, status); - if (status) - break; + if (status) { + OCIDescriptorFree(colhd, OCI_DTYPE_PARAM); + break; + } if (DBIS->debug >= 3) PerlIO_printf(DBILOGFP, " 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) + if (col_dbtype != SQLT_CLOB && col_dbtype != SQLT_BLOB) { + OCIDescriptorFree(colhd, OCI_DTYPE_PARAM); continue; + } if (!lob_cols_hv) lob_cols_hv = newHV(); sv = newSViv(col_dbtype); - (void)sv_setpvn(sv, col_name, col_name_len); +/* (void)sv_setpvn(sv, col_name, col_name_len); #ifdef UTF8_SUPPORT DBD_SET_UTF8(sv); #endif - (void)SvIOK_on(sv); /* what a wonderful hack! */ + (void)SvIOK_on(sv);*/ /* what a wonderful hack! */ hv_store(lob_cols_hv, col_name,col_name_len, sv,0); + OCIDescriptorFree(colhd, OCI_DTYPE_PARAM); } + OCIHandleFree(dschp, OCI_HTYPE_DESCRIBE); if (status != OCI_SUCCESS) { - OCIHandleFree_log_stat(dschp, OCI_HTYPE_DESCRIBE, status); + if (tablename != new_tablename) + free(tablename); return oci_error(sth, errhp, status, "OCIDescribeAny/OCIParamGet/OCIAttrGet/LOB refetch"); } - if (!lob_cols_hv) + if (!lob_cols_hv) { + if (tablename != new_tablename) + free(tablename); 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 @@ -1694,8 +1711,9 @@ else { /* got a type match - check it's safe */ SV *sv_other; char *p_other; + I32 i_other; /* would any other lob field match this type? */ - while( (sv_other = hv_iternextsv(lob_cols_hv, &p_other, &i)) != NULL ) { + while( (sv_other = hv_iternextsv(lob_cols_hv, &p_other, &i_other)) != NULL ) { if (phs->ftype != SvIV(sv_other)) continue; if (DBIS->debug >= 3) @@ -1703,6 +1721,10 @@ " 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); + sv_free(sql_select); + if (tablename != new_tablename) + free(tablename); return oci_error(sth, errhp, OCI_ERROR, "Need bind_param(..., { ora_field=>... }) attribute to identify table LOB field names"); } @@ -1716,7 +1738,7 @@ " 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); - hv_delete(lob_cols_hv, p, i, 0); + hv_delete(lob_cols_hv, p, i, G_DISCARD); fbh = &lr->fbh_ary[lr->num_fields++]; fbh->name = phs->name; fbh->ftype = phs->ftype; @@ -1734,8 +1756,12 @@ phs->name, phs->ftype); } } + sv_free((SV*)lob_cols_hv); if (unmatched_params) { Safefree(lr); + sv_free(sql_select); + if (tablename != new_tablename) + free(tablename); return oci_error(sth, errhp, OCI_ERROR, "Can't match some parameters to LOB fields in the table, check type and name"); } @@ -1746,6 +1772,8 @@ if (DBIS->debug >= 3) PerlIO_printf(DBILOGFP, " lob refetch sql: %s\n", SvPVX(sql_select)); + if (tablename != new_tablename) + free(tablename); lr->sql_select = sql_select; lr->stmthp = NULL; @@ -1759,15 +1787,24 @@ OCIStmtPrepare_log_stat(lr->stmthp, errhp, (text*)SvPVX(sql_select), SvCUR(sql_select), OCI_NTV_SYNTAX, OCI_DEFAULT, status); - if (status != OCI_SUCCESS) + if (status != OCI_SUCCESS) { + OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT); + sv_free(lr->sql_select); + Safefree(lr); return oci_error(sth, errhp, status, "OCIStmtPrepare/LOB refetch"); + } /* bind the rowid input */ OCIDescriptorAlloc_ok(imp_sth->envhp, &lr->rowid, OCI_DTYPE_ROWID); OCIBindByName_log_stat(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) + if (status != OCI_SUCCESS) { + OCIDescriptorFree(lr->rowid, OCI_DTYPE_ROWID); + OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT); + sv_free(lr->sql_select); + Safefree(lr); return oci_error(sth, errhp, status, "OCIBindByPos/LOB refetch"); + } /* define the output fields */ for(i=0; i < lr->num_fields; ++i) { @@ -1788,8 +1825,15 @@ OCIDefineByPos_log_stat(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) + if (status != OCI_SUCCESS) { + OCIDescriptorFree(lr->rowid, OCI_DTYPE_ROWID); + OCIHandleFree(lr->stmthp, OCI_HTYPE_STMT); + sv_free(lr->sql_select); + Safefree(lr); + fb_ary_free(fbh->fb_ary); + fbh->fb_ary = NULL; return oci_error(sth, errhp, status, "OCIDefineByPos/LOB refetch"); + } } imp_sth->lob_refetch = lr; /* structure copy */ @@ -1884,6 +1928,8 @@ } sv_free(lr->sql_select); sv_free(lr->fbh_ary_sv); + if (lr->rowid) + OCIDescriptorFree(lr->rowid, OCI_DTYPE_ROWID); Safefree(imp_sth->lob_refetch); imp_sth->lob_refetch = NULL; }
I'm applying it (in modified form) for DBD::Oracle 1.16. Please retest when it's released. Thanks!