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" <[EMAIL PROTECTED]>
To: "Christoph Haller" <[EMAIL PROTECTED]>
Cc: <[EMAIL PROTECTED]>
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
>


---------------------------(end of broadcast)---------------------------
TIP 9: the planner will ignore your desire to choose an index scan if your
      joining column's datatypes do not match

Reply via email to