Re: Prepared select

From: "Cyril VELTER" <cyril(dot)velter(at)metadys(dot)com>
To: "Robert Turnbull" <rturnbull(at)strategicmind(dot)com>
Cc: <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Prepared select
Date: 2004-04-20 15:01:38
Message-ID: 062c01c426e8$62255b20$f901a8c0@cvfixe
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

libpq doesn't have enought support to allow executing a prepared statement
in a named portal (current libpq only works wuth the unnamed portal). But
the V3 protocol have it. I solved this problem by adding the following
functions. They let you prepare a named statement, execute this statement in
a named portal, fetch from it and close it.

this is a temporary solution to wait for an official extension of libpq
(more call could be added to support completly the V3 protocol).

cyril

/*
* PQportalSetup
* Setup a portal to execute a prepared statement
*/
PGresult *
PQportalSetup(PGconn *conn,
const char *stmtName,
const char *portalName,
int nParams,
const char *const * paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat)
{
int i;

if (!PQexecStart(conn))
return NULL;

if (!PQsendQueryStart(conn))
return NULL;

if (!stmtName)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("statement name is a null pointer\n"));
return NULL;
}

/* This isn't gonna work on a 2.0 server */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("function requires at least protocol version 3.0\n"));
return 0;
}

/* construct the Bind message */
if (pqPutMsgStart('B', false, conn) < 0 ||
pqPuts(portalName, conn) < 0 ||
pqPuts(stmtName, conn) < 0)
goto sendFailed;
if (nParams > 0 && paramFormats)
{
if (pqPutInt(nParams, 2, conn) < 0)
goto sendFailed;
for (i = 0; i < nParams; i++)
{
if (pqPutInt(paramFormats[i], 2, conn) < 0)
goto sendFailed;
}
}
else
{
if (pqPutInt(0, 2, conn) < 0)
goto sendFailed;
}
if (pqPutInt(nParams, 2, conn) < 0)
goto sendFailed;
for (i = 0; i < nParams; i++)
{
if (paramValues && paramValues[i])
{
int nbytes;

if (paramFormats && paramFormats[i] != 0)
{
/* binary parameter */
nbytes = paramLengths[i];
}
else
{
/* text parameter, do not use paramLengths */
nbytes = strlen(paramValues[i]);
}
if (pqPutInt(nbytes, 4, conn) < 0 ||
pqPutnchar(paramValues[i], nbytes, conn) < 0)
goto sendFailed;
}
else
{
/* take the param as NULL */
if (pqPutInt(-1, 4, conn) < 0)
goto sendFailed;
}
}
if (pqPutInt(1, 2, conn) < 0 ||
pqPutInt(resultFormat, 2, conn))
goto sendFailed;
if (pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* construct the Sync message */
if (pqPutMsgStart('S', false, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* remember we are using extended query protocol */
conn->ext_query = true;

/*
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQgetResult() will do any additional
* flushing needed.
*/
if (pqFlush(conn) < 0)
goto sendFailed;

/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;

return PQexecFinish(conn);

sendFailed:
pqHandleSendFailure(conn);
return NULL;
}

/*
* PQportalFetch
* Fetch next rows
*/
PGresult *
PQportalFetch(PGconn *conn,
const char *portalName,
int maxrows)
{
if (!PQexecStart(conn))
return NULL;

if (!PQsendQueryStart(conn))
return NULL;

/* This isn't gonna work on a 2.0 server */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("function requires at least protocol version 3.0\n"));
return 0;
}

/* construct the Describe Portal message */
if (pqPutMsgStart('D', false, conn) < 0 ||
pqPutc('P', conn) < 0 ||
pqPuts(portalName, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* construct the Execute message */
if (pqPutMsgStart('E', false, conn) < 0 ||
pqPuts(portalName, conn) < 0 ||
pqPutInt(maxrows, 4, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* construct the Sync message */
if (pqPutMsgStart('S', false, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* remember we are using extended query protocol */
conn->ext_query = true;

/*
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQgetResult() will do any additional
* flushing needed.
*/
if (pqFlush(conn) < 0)
goto sendFailed;

/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
return PQexecFinish(conn);

sendFailed:
pqHandleSendFailure(conn);
return NULL;
}

/*
* PQportalClose
* Close a named portal
* using protocol 3.0
*/
PGresult *
PQportalClose(PGconn *conn,
const char *portalName)
{
if (!PQexecStart(conn))
return NULL;

if (!PQsendQueryStart(conn))
return NULL;

/* This isn't gonna work on a 2.0 server */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("function requires at least protocol version 3.0\n"));
return 0;
}

/* construct the Close message */
if (pqPutMsgStart('C', false, conn) < 0 ||
pqPutc('P', conn) < 0 ||
pqPuts(portalName, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* construct the Sync message */
if (pqPutMsgStart('S', false, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;

/* remember we are using extended query protocol */
conn->ext_query = true;

/*
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQgetResult() will do any additional
* flushing needed.
*/
if (pqFlush(conn) < 0)
goto sendFailed;

/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
return PQexecFinish(conn);

sendFailed:
pqHandleSendFailure(conn);
return NULL;
}

----- Original Message -----
From: "Robert Turnbull" <rturnbull(at)strategicmind(dot)com>
To: "Christoph Haller" <ch(at)rodos(dot)fzk(dot)de>
Cc: <pgsql-hackers(at)postgresql(dot)org>
Sent: Monday, April 19, 2004 2:48 AM
Subject: Re: [HACKERS] Prepared select

> There are several production issues related to the proposed solution. For
> example, what happens when the result set exceeds the swap space of the
> server or client machine? My original question is how to get a cursor from
a
> prepared select so a subset of the result can be returned to the client
for
> processing. For your solution to work the SQL EXECUTE command needs the
> functionality of the SQL FETCH command.
>
>
> > >
> > >
> > > How can I use a prepared select statement as mentioned in the
> documentation=
> > > on SQL PREPARE. Preparing the statement is easy, the problem is using
> the =
> > > plan to get a cursor. My assumption is the SQL OPEN command is not
> document=
> > > ed or there is some other libpq API to make this happen.
> > >
> > > Thanks
> > >
> > >
> > >
> > I'm using libpq and lines like below are working:
> >
> > res = PQexec(conn,
> > "PREPARE plan001 ( integer , double precision , character ) AS SELECT
> a,b,d FROM foo WHERE a = $1 OR d > $2 OR b = $3");
> > ...
> > res = PQexec(conn, "EXECUTE plan001 ( 3 , 6.66 , 'whatever' ) ");
> >
> > HTH, pretty late reply - I know (but no one else did as far as I can
tell)
> >
> > Regards, Christoph
> >
> >
> >
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 8: explain analyze is your friend
>

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrew Dunstan 2004-04-20 15:04:24 Re: COPY CSV keywords
Previous Message Rod Taylor 2004-04-20 14:54:30 Re: ERROR action extension for rules?