Re: ESQL/C vs. SQLDA
Posted in 1998
On 20 Jun 1998 steve53@earthlink.net wrote: > I've got an ESQL/C application (Solaris, Informix SE v7.2). There are > cases where I need to rebuild the SELECT statments with the same > fields list but a different query. In an effort to wring out a bit > more performance, I decided that I could avoid the DESCRIBE > overhead and reuse the fields clause SQLDA since the fields list > never changed. This worked well until I discovered that Informix > creates an SQLDA that contains pointers (i.e. sqlname) to data outside > the SQLDA structure allocated by Informix. > > Did I miss something? Yes, No, Maybe... > If so, would someone to point me at the documentation that says I am > required to do a DESCRIBE after 'every' PREPARE? The data in sqlname is, empirically, released when the described statement is freed with EXEC SQL FREE. The space in the sqlda structure given to you by EXEC SQL DESCRIBE is not released at that time. This is weird, but so. I was testing with 7.24.UC1 on Solaris 2.6, but I think this is the way it has been for a while. Now, there is very limited documentation on when which space is released. However, it is reasonable to expect that when the statement is released, the space associated with it is released, apart from the described SQLDA. It is not clearly documented either way what happens to the names; as I discovered using a debugging malloc, the string space is allocated in a single malloc() during the prepare phase -- not the describe phase -- and is released when the prepared statement is released. In your shoes, I'd simply arrange to copy the string space for the sqlname parts of the SQLDA structure and then rejig the sqlname pointers to point to your copy of the space. Note that there are multiple strings in it, each one of which is null terminated. You could probably do this with code such as: void *localize_sqlname(struct sqlda *sqlda) { char *new; char *lastname = sqlda->sqlvar[sqlda->sqld-1].sqlname; char *firstname = sqlda->sqlvar[0].sqlname; /* Double check for off-by-one errors before using */ size_t nbytes = lastname + strlen(lastname) + 1 - firstname; new = malloc(nbytes); if (new != 0) { memcpy(new, firstname, nbytes); for (i = 0; i < sqlda->sqld; i++) { sqlda->sqlvar[i].sqlname = new + (sqlda->sqlvar[i].sqlname - firstname); } } return new; } The return value is either the space that needs to be released or 0 if the memory allocation failed. Hence: EXEC SQL DESCRIBE p_stmt INTO mysqlda; space = localize_sqlda(mysqlda); ... free(mysqlda); free(space); Yours, Jonathan Leffler (jleffler@informix.com) #include <witticism.h> Guardian of DBD::Informix -- see http://www.perl.com/CPAN