Hi,

On Mar 25 08:47, John DeSoi wrote:
> I have not looked at libpq in any detail, but it should have access  
> to the type of all the parameters in the prepared statement. The  
> Describe (F) statement in the frontend/backend protocol identifies  
> the type of each parameter.

I've prepared a patch for the Describe <-> ParameterDescription
messaging which is available via current extended query protocol.

Usage (and implementation) is explained in the patch's documentation
related part. (Also I tried to place informative comments in the code
too.)

But I've a problem with ereport() error calls caused by erronous
target_type entries. After an error in exec_describe_statement_message()
(or exec_describe_portal_message()) it leaves block with ereport() call
and client side stalls in PGASYNC_BUSY state while backend stalls in
PostgresMain() by calling ReadCommand(). To summerize, an error
returning pqDescribe() call causes both side to stall.

I'd be so appreciated to hear your thoughts about the patch and above
problem.


Regards.
? src/interfaces/libpq/.deps
? src/interfaces/libpq/cscope.out
? src/interfaces/libpq/libpq.so.4.2
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.206
diff -c -r1.206 libpq.sgml
*** doc/src/sgml/libpq.sgml     10 Mar 2006 19:10:48 -0000      1.206
--- doc/src/sgml/libpq.sgml     1 Apr 2006 18:27:41 -0000
***************
*** 2045,2053 ****
  </varlistentry>
  
  <varlistentry>
! <term><function>PQprint</function><indexterm><primary>PQprint</></></term>
  <listitem>
  <para>
            Prints out all the rows and,  optionally,  the
            column names  to  the specified output stream.
  <synopsis>
--- 2045,2081 ----
  </varlistentry>
  
  <varlistentry>
! 
<term><function>PQdescPrepared</function><indexterm><primary>PQdescPrepared</></></term>
! 
<term><function>PQdescPortal</function><indexterm><primary>PQdescPortal</></></term>
  <listitem>
  <para>
+       Describe a prepared statement or portal.
+       <synopsis>int PQdescPrepared(PGconn *conn, const char *stmt);</synopsis>
+       <synopsis>int PQdescPortal(PGconn *conn, const char *portal);</synopsis>
+ </para>
+ <para>
+       These two functions are used to describe a prepared statement or portal.
+       Functions return 1 on success and 0 on failure. (An appropriate error
+       message will be placed in an error situation.) <symbol>NULL</> values in
+       the <parameter>stmt</> and <parameter>portal</> parameters will be 
treated
+       as an empty string.
+ </para>
+ <para>
+       After a <function>PQdescPrepared</> or <function>PQdescPortal</> 
function
+       call, issuing a <function>PQgetResult</> will place backend's response
+       into a <structname>PGresult</structname> structure - that'll be
+       extractable via <function>PQftype</>.
+ </para>
+ <para>
+       These functions are available within extended query protocol which is
+       available in version 3.0 and above.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term><function>PQprint</function><indexterm><primary>PQprint</></></term>
+ <para>
            Prints out all the rows and,  optionally,  the
            column names  to  the specified output stream.
  <synopsis>
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.482
diff -c -r1.482 postgres.c
*** src/backend/tcop/postgres.c 14 Mar 2006 22:48:21 -0000      1.482
--- src/backend/tcop/postgres.c 1 Apr 2006 18:28:08 -0000
***************
*** 3391,3396 ****
--- 3391,3398 ----
                                                                           
describe_type)));
                                                        break;
                                        }
+                               
+                                       send_ready_for_query = true;
                                }
                                break;
  
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.182
diff -c -r1.182 fe-exec.c
*** src/interfaces/libpq/fe-exec.c      14 Mar 2006 22:48:23 -0000      1.182
--- src/interfaces/libpq/fe-exec.c      1 Apr 2006 18:28:15 -0000
***************
*** 55,60 ****
--- 55,62 ----
  static void parseInput(PGconn *conn);
  static bool PQexecStart(PGconn *conn);
  static PGresult *PQexecFinish(PGconn *conn);
+ static int pqDescribe(PGconn *conn, const char desc_type,
+                                         const char *desc_target);
  
  
  /* ----------------
***************
*** 2281,2286 ****
--- 2283,2400 ----
                return 0;
  }
  
+ 
+ /*
+  * pqDescribe - Describe given prepared statement or portal.
+  *
+  * Available options for target_type are
+  *   's' to describe a prepared statement; or
+  *   'p' to describe a portal.
+  * Returns 1 on success and 0 on failure.
+  *
+  * By issuing a PQgetResult(), response from the server will be placed
+  * in an empty PGresult that will be extractable via PQftype().
+  */
+ static int
+ pqDescribe(PGconn *conn, const char desc_type, const char *desc_target)
+ {
+       int     ret;
+ 
+       /* 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;
+       }
+       
+       if (!conn)
+               return 0;
+       
+       /* Clear the connection error message. */
+       resetPQExpBuffer(&conn->errorMessage);
+ 
+       /* Don't try to send if we know there's no live connection. */
+       if (conn->status != CONNECTION_OK)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no connection 
to the server\n"));
+               return false;
+       }
+       
+       /* Can't send while already busy, either. */
+       if (conn->asyncStatus != PGASYNC_IDLE)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("another 
command is already in progress\n"));
+               return false;
+       }
+ 
+       /* Initialize async result-accumulation state. */
+       conn->result = NULL;
+       conn->curTuple = NULL;
+       
+       if (desc_target)
+       {
+               int             len = strlen(desc_target) + 2;
+               char    buf[len];
+ 
+               snprintf(buf, len, "%c%s", desc_type, desc_target);
+ 
+               /*
+                * Flushing stuff will be handled by pqPacketSend(). (Although
+                * this theoretically could block, it really shouldn't for
+                * these kind of small messages.)
+                */
+               ret = pqPacketSend(conn, 'D', buf, len);
+       }
+       
+       /* NULL desc_target values will be treated as an empty string. */
+       else
+       {
+               int             len = 2;
+               char    buf[len];
+ 
+               buf[0] = desc_type;
+               buf[1] = '\0';
+               ret = pqPacketSend(conn, 'D', buf, len);
+       }
+ 
+       /* In case of a pqPacketSend() failure... */
+       if (ret != STATUS_OK)
+       {
+               pqHandleSendFailure(conn);
+               return 0;
+       }
+ 
+       /* Remember we are using extended query protocol. */
+       conn->queryclass = PGQUERY_EXTENDED;
+ 
+       /* Free last query string. */
+       if (conn->last_query)
+       {
+               free(conn->last_query);
+               conn->last_query = NULL;
+       }
+ 
+       /* Describe request is sent. */
+       conn->asyncStatus = PGASYNC_BUSY;
+       return 1;
+ }
+ 
+ int
+ PQdescPrepared(PGconn *conn, const char *stmt)
+ {
+       return pqDescribe(conn, 'S', stmt);
+ }
+ 
+ int
+ PQdescPortal(PGconn *conn, const char *portal)
+ {
+       return pqDescribe(conn, 'P', portal);
+ }
+ 
+ 
  /* PQsetnonblocking:
   *    sets the PGconn's database connection non-blocking if the arg is TRUE
   *    or makes it non-blocking if the arg is FALSE, this will not protect
***************
*** 2346,2351 ****
--- 2460,2466 ----
        free(ptr);
  }
  
+ 
  /*
   * PQfreeNotify - free's the memory associated with a PGnotify
   *
Index: src/interfaces/libpq/fe-protocol3.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v
retrieving revision 1.26
diff -c -r1.26 fe-protocol3.c
*** src/interfaces/libpq/fe-protocol3.c 14 Mar 2006 22:48:23 -0000      1.26
--- src/interfaces/libpq/fe-protocol3.c 1 Apr 2006 18:28:22 -0000
***************
*** 50,55 ****
--- 50,56 ----
  static int    getNotify(PGconn *conn);
  static int    getCopyStart(PGconn *conn, ExecStatusType copytype);
  static int    getReadyForQuery(PGconn *conn);
+ static int    getParamDescriptions(PGconn *conn);
  static void reportErrorPosition(PQExpBuffer msg, const char *query,
                                                                int loc, int 
encoding);
  static int build_startup_packet(const PGconn *conn, char *packet,
***************
*** 350,355 ****
--- 351,375 ----
                                         * the COPY command.
                                         */
                                        break;
+                               case 't':               /* Parameter 
Description */
+                                       if (!conn->result)
+                                       {
+                                               if 
(!getParamDescriptions(conn))        /* Success. */
+                                                       break;
+                                               else                            
                                /* Not enough data or an error. */
+                                                       return;
+                                       }
+                                       else
+                                       {
+                                               /*
+                                                * We'll use a new PGresult to 
place 't' input. Thus,
+                                                * we stop parsing until the 
application accepts the
+                                                * current result.
+                                                */
+                                               conn->asyncStatus = 
PGASYNC_READY;
+                                               return;
+                                       }
+                                       break;
                                default:
                                        printfPQExpBuffer(&conn->errorMessage,
                                                                          
libpq_gettext(
***************
*** 1154,1159 ****
--- 1174,1249 ----
  }
  
  /*
+  * getParamDescriptions - process ParameterDescription message.
+  *
+  * We'll place parsed message information into a new PGresult. (Only
+  * parameter description attributes are going to be reliable.)
+  * Returns 0 if parsing is completed, 1 if there isn't enough data yet
+  * and -1 if there occurs an error.
+  */
+ static int
+ getParamDescriptions(PGconn *conn)
+ {
+       PGresult        *res;
+       int                      nattr;
+       int                      i;
+       Oid                      typid;
+ 
+       res = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
+       if (!res)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("out of 
memory\n"));
+               goto Error;
+       }
+       
+       /*
+        * parseInput() already read the 't' label and message length.
+        * The next byte is the number of parameters used by the
+        * statement (may be zero).
+        */
+       if (pqGetInt(&nattr, 2, conn) < 0)
+               goto NotEnoughData;
+       res->numAttributes = nattr;
+ 
+       /* Allocate space for the attribute descriptors. */             
+       if (nattr > 0)
+       {
+               int sz = nattr * sizeof(PGresAttDesc);
+               
+               res->attDescs = (PGresAttDesc *) pqResultAlloc(res, sz, TRUE);
+               if (!res->attDescs)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("out of 
memory\n"));
+                       goto Error;
+               }
+               
+               MemSet(res->attDescs, 0, sz);
+       }
+ 
+       /* Loop through attribute's type OIDs. */
+       for (i = 0; i < nattr; i++)
+       {
+               if (pqGetInt(&typid, 4, conn) < 0)
+                       goto NotEnoughData;
+               res->attDescs[i].typid = typid;
+       }
+ 
+       /* Success. */
+       conn->result = res;
+       return 0;
+ 
+ NotEnoughData:
+       PQclear(res);
+       return 1;
+ 
+ Error:
+       PQclear(res);
+       return -1;
+ }
+ 
+ /*
   * PQgetCopyData - read a row of data from the backend during COPY OUT
   *
   * If successful, sets *buffer to point to a malloc'd row of data, and
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.126
diff -c -r1.126 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h     20 Mar 2006 15:07:05 -0000      1.126
--- src/interfaces/libpq/libpq-fe.h     1 Apr 2006 18:28:25 -0000
***************
*** 414,419 ****
--- 414,423 ----
  extern int    PQgetlength(const PGresult *res, int tup_num, int field_num);
  extern int    PQgetisnull(const PGresult *res, int tup_num, int field_num);
  
+ /* Describe prepared statements or portals. */
+ extern int    PQdescPrepared(PGconn *conn, const char *stmt);
+ extern int    PQdescPortal(PGconn *conn, const char *portal);
+ 
  /* Delete a PGresult */
  extern void PQclear(PGresult *res);
  
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to