On Sat, 14 Mar 2020 at 02:34, Nick Kew <[email protected]> wrote:
> Isn't your most obvious fix simply to allocate your row
> from your choice of pool before the first get_row?
That wouldn't work. Here's the start of the dbd_oracle_get_row
function in the apr_dbd_oracle driver:
static int dbd_oracle_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
apr_dbd_row_t **rowp, int rownum)
{
apr_dbd_row_t *row = *rowp;
apr_dbd_t *sql = res->handle;
int_errorcode;
if (row == NULL) {
row = apr_palloc(pool, sizeof(apr_dbd_row_t));
*rowp = row;
row->res = res;
/* Oracle starts counting at 1 according to the docs */
row->n = res->seek ? rownum : 1;
row->pool = pool;
}
else {
if (res->seek) {
row->n = rownum;
}
else {
++row->n;
}
}
so as well as allocating a apr_dbd_row_t if one does not exist it is
also setting up some of the members. If I allocated it myself from a
different pool it would not be initialised. Compare, though, with the
equivalent section of the dbd_mysql_get_row function from the
apr_dbd_mysql driver:
if (ret == 0) {
if (!*row) {
*row = apr_palloc(pool, sizeof(apr_dbd_row_t));
}
(*row)->row = r;
(*row)->res = res;
(*row)->len = mysql_fetch_lengths(res->res);
}
else {
apr_pool_cleanup_run(res->pool, res->res, free_result);
}
return ret;
so with that in mind I thought I'd compare what apr_dbd_get_entry does
in each of those two drivers. In the Oracle driver the a sufficient
fragment is:
default:
buf = apr_pstrndup(row->pool, val->buf.sval, val->len);
break;
}
return (const char*) buf;
}
so the Oracle driver is returning a copy of the value, allocated from
a pool remember from the call to apr_dbd_get_row. The equivalent from
the MySQL driver:
if (row->res->statement) {
bind = &row->res->bind[n];
if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
return NULL;
}
if (*bind->is_null) {
return NULL;
}
else {
return bind->buffer;
}
}
else {
return row->row[n];
}
return NULL;
so this appears to be returning a pointer into something linked to the
apr_dbd_row_t which is presumably re-used for each row fetched as long
as the apr_dbd_row_t remains allocated. In fact this seems much
easier for the library user to control - want long lifetime values
from apr_dbd_get_entry then reset the apr_dbd_row_t pointer to NULL
each iteration and it will achieve that. Want to keep memory
consumption down then leave the pointer alone and the memory will be
re-used.
So maybe the real issue is that the two drivers have interpreted what
is required differently. There is nothing in the Doxygen
documentation for apr_dbd_get_entry about the lifetime of the value
returned. Is there guidance or a standard for driver writers
elsewhere?