Last minute mini-proposal (I know, I know) for PQexecf() - Mailing list pgsql-hackers

From
Subject Last minute mini-proposal (I know, I know) for PQexecf()
Date
Msg-id 1175299673.6784.26.camel@sakai.localdomain
Whole thread Raw
Responses Re: Last minute mini-proposal (I know, I know) for PQexecf()  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: Last minute mini-proposal (I know, I know) for PQexecf()  (Gregory Stark <stark@enterprisedb.com>)
List pgsql-hackers
While cleaning up some pg_migrator code (<a
href="http://pgfoundry.org/projects/pg-migrator/)">http://pgfoundry.org/projects/pg-migrator/)</a>it occurred to me
thata typical libpq client application spends a lot of code constructing SQL commands.  The code typically looks like
this:<br/><br />     a) allocate enough room to hold the command             <br />     b) sprintf( command, text,
argument,argument, argument, ... )<br />     c) PQexec( conn, command )<br />     d) free( command )<br /><br /> In
mostcases, the amount of memory that you allocate in step a) is just an educated guess.  It's typically more room than
youneed, occassionally less room than you need (and you get a buffer overflow exploit), and it's rarely maintained
properlywhen you modify the command text (or the argument list).<br /><br /> I'd like to see a new variant on
PQexec():<br/><br />     PGresult * PQexecf(PGconn *conn, const char *fmt, ...);<br /><br /> PQexecf() simply performs
stepsa, b, c, and d for you. And you call it like this:<br /><br />     PQexecf( conn, text, argument, argument,
argument,... )<br /><br /> PQexecf() is just a wrapper around the already existing createPQExpBuffer(),
enlargePQExpBuffer(),printfPQExpBuffer(), and PQexec() so it introduces no new code (other than assembling the wrapper)
anddoesn't change any existing code.  PQexecf() is similar to PQexecParams() but it much simpler to use (and should be
veryfamiliar to C programmers).  PQexecf() is not intended as a replacement for PQprepare() and PQexecPrepared() - you
shoulduse prepare/exec when you want to execute a command many times.<br /><br /> I could eliminate a lot of
client-sidecode if PQexecf() were available - and the code that I could remove is the code that's most likely to be
buggyand least likely to be properly maintained.<br /><br /> I've thrown together an UNTESTED prototype (below), just
toget the idea across - you'll recognize that most of this code is identical to printPQExpBuffer().  In the prototype,
I'mkeeping a static PQExpBuffer that grows to the hold the largest string ever required by the client application -
thatpart seems to be a point for discussion, but since the detail is hidden in the implementation, we could adjust the
codelater if necessary (without changing the interface).<br /><br /> Of course, I could include an implementation of
PQexecf()in each of my client applications if it were not available in libpq, but that would be silly and I'd have to
inventmy own createPQExpBuffer() / enlargePQExpBuffer() code since those functions are not an official part of libpq
(andwon't even be available to a Win32 client application).<br /><br /> Is it just too late to even think about this
for8.3? (Bruce laughed at me when I suggested the idea :-)  <br /><br />            -- Korry<br />               <a
href="mailto:korryd@enterprisedb.com">korryd@enterprisedb.com</a><br/>               <a
href="http://www.enterprisedb.com">http://www.enterprisedb.com</a><br/><br /><hr noshade="NOSHADE" /><br /> PGresult
*<br/> PQexecf(PGconn *conn, const char *fmt, ...)<br /> {<br /> static PQExpBuffer str;<br /> va_list       args;<br
/><br/> if (str == NULL)<br /> str = createPQExpBuffer();<br /><br /> for (;;)<br /> {<br />         /*<br /> * Try to
formatthe given string into the available space; but if<br /> * there's hardly any space, don't bother trying, just
fallthrough to<br /> * enlarge the buffer first.<br /> */<br /> if (str->maxlen > str->len + 16)<br /> {<br />
size_tavail = str->maxlen - str->len - 1;<br /> int    nprinted;<br /><br /> va_start(args, fmt);<br /> nprinted
=vsnprintf(str->data + str->len, avail, fmt, args);<br /> va_end(args);<br /><br /> /*<br /> * Note: some
versionsof vsnprintf return the number of chars<br /> * actually stored, but at least one returns -1 on failure. Be<br
/>* conservative about believing whether the print worked.<br /> */<br /> if (nprinted >= 0 && nprinted <
(int)avail - 1)<br /> {<br /> /* Success.  Note nprinted does not include trailing null. */<br /> str->len +=
nprinted;<br/> break;<br /> }<br /> }<br /> /* Double the buffer size and try again. */<br /> if
(!enlargePQExpBuffer(str,str->maxlen))<br /> return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); /* oops, out of
memory*/<br /> }<br /><br /> return PQexec(conn, str->data);<br /> }<br /><br /> 

pgsql-hackers by date:

Previous
From: "Florian G. Pflug"
Date:
Subject: Re: Minor changes to Recovery related code
Next
From: Tom Lane
Date:
Subject: Re: Last minute mini-proposal (I know, I know) for PQexecf()