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;
}