On Jun 16 08:21, Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Volkan YAZICI wrote:
> >> The problem is, AFAICS, it's not possible to distinguish between a tuple
> >> returning query (T, ..., C, Z or T, E) and a description of a portal (T,
> >> Z). Therefore, I've created a global flag (parsing_row_desc) which is
> >> turned on when we receive a 'T' and turned off if we receive a 'C' or
> >> 'E'. It's a kind of ugly method but the only solution I could come up
> >> with.
> 
> > The problem with this solution is that it is not thread-safe.  Perhaps
> > you can use a per-PGconn boolean?

Ie replaced the static flag with a conn->queryclass value using
PGQueryClass as Tom suggested. Also updated patch to be compatible with
exports.txt stuff.

> The whole thing sounds like brute force to me.  Shouldn't you be adding
> states to enum PGQueryClass, if you need to track what sort of Describe
> you're doing?

I totally agree with the followed ugly style. But IMHO the recursive
parsing (that is followed in pqParseInputN()) of received data is the main
problem behind this. I think, it will even get harder everytime somebody
try to to add another type of message parsing capability to that loop.
For instance, isn't pollution of PGQueryClass with state variables (like
PGQUERY_PREPARE or PGQUERY_DESCRIBE) one of the proofs of this.

While playing with pqParseInputN loops, I feel like coding Lisp recursions
using C syntax; it's quite ridiculous.


Regards.
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.489
diff -c -r1.489 postgres.c
*** src/backend/tcop/postgres.c 20 Jun 2006 22:52:00 -0000      1.489
--- src/backend/tcop/postgres.c 24 Jun 2006 11:31:10 -0000
***************
*** 1853,1858 ****
--- 1853,1859 ----
  static void
  exec_describe_statement_message(const char *stmt_name)
  {
+       MemoryContext   oldContext;
        PreparedStatement *pstmt;
        TupleDesc       tupdesc;
        ListCell   *l;
***************
*** 1865,1871 ****
        start_xact_command();
  
        /* Switch back to message context */
!       MemoryContextSwitchTo(MessageContext);
  
        /* Find prepared statement */
        if (stmt_name[0] != '\0')
--- 1866,1872 ----
        start_xact_command();
  
        /* Switch back to message context */
!       oldContext = MemoryContextSwitchTo(MessageContext);
  
        /* Find prepared statement */
        if (stmt_name[0] != '\0')
***************
*** 1923,1929 ****
--- 1924,1933 ----
                                                                  NULL);
        else
                pq_putemptymessage('n');        /* NoData */
+       
+       MemoryContextSwitchTo(oldContext);
  
+       finish_xact_command();
  }
  
  /*
***************
*** 1934,1939 ****
--- 1938,1944 ----
  static void
  exec_describe_portal_message(const char *portal_name)
  {
+       MemoryContext   oldContext;
        Portal          portal;
  
        /*
***************
*** 1943,1949 ****
        start_xact_command();
  
        /* Switch back to message context */
!       MemoryContextSwitchTo(MessageContext);
  
        portal = GetPortalByName(portal_name);
        if (!PortalIsValid(portal))
--- 1948,1954 ----
        start_xact_command();
  
        /* Switch back to message context */
!       oldContext = MemoryContextSwitchTo(MessageContext);
  
        portal = GetPortalByName(portal_name);
        if (!PortalIsValid(portal))
***************
*** 1975,1980 ****
--- 1980,1989 ----
                                                                  
portal->formats);
        else
                pq_putemptymessage('n');        /* NoData */
+       
+       MemoryContextSwitchTo(oldContext);
+ 
+       finish_xact_command();
  }
  
  
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.11
diff -c -r1.11 exports.txt
*** src/interfaces/libpq/exports.txt    28 May 2006 22:42:05 -0000      1.11
--- src/interfaces/libpq/exports.txt    24 Jun 2006 11:31:10 -0000
***************
*** 130,132 ****
--- 130,134 ----
  PQencryptPassword         128
  PQisthreadsafe            129
  enlargePQExpBuffer        130
+ PQdescribePrepared        131
+ PQdescribePortal          132
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.186
diff -c -r1.186 fe-exec.c
*** src/interfaces/libpq/fe-exec.c      28 May 2006 21:13:54 -0000      1.186
--- src/interfaces/libpq/fe-exec.c      24 Jun 2006 11:31:12 -0000
***************
*** 61,66 ****
--- 61,68 ----
  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);
  
  
  /* ----------------
***************
*** 2304,2309 ****
--- 2306,2415 ----
                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 0 on success and 1 on failure.
+  *
+  * By issuing a PQgetResult(), response from the server will be placed
+  * in an empty PGresult which will be extractable via PQf*() function family.
+  */
+ 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 1;
+       }
+       
+       if (!conn)
+               return 1;
+       
+       /* 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 1;
+       }
+       
+       /* 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 1;
+       }
+ 
+       /* Initialize async result-accumulation state. */
+       conn->result = NULL;
+       conn->curTuple = NULL;
+       
+       if (desc_target)
+               ret = (pqPutMsgStart('D', false, conn) < 0 ||
+                          pqPutc(desc_type, conn) < 0 ||
+                          pqPuts(desc_target, conn) < 0 ||
+                          pqPutMsgEnd(conn) < 0);
+       
+       /* NULL desc_target values will be treated as an empty string. */
+       else
+               ret = (pqPutMsgStart('D', false, conn) < 0 ||
+                          pqPutc(desc_type, conn) < 0 ||
+                          pqPutc('\0', conn) < 0 ||
+                          pqPutMsgEnd(conn) < 0);
+       
+       if (ret)
+               goto SendFailure;
+       
+       /* Extended protocol requires a Sync message. */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto SendFailure;
+ 
+       /* Remember we'll parse a Describe response. */
+       conn->queryclass = PGQUERY_DESCRIBE;
+ 
+       /* 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 0;
+ 
+ SendFailure:
+       pqHandleSendFailure(conn);
+       return 1;
+ }
+ 
+ int
+ PQdescribePrepared(PGconn *conn, const char *stmt)
+ {
+       return pqDescribe(conn, 'S', stmt);
+ }
+ 
+ int
+ PQdescribePortal(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
***************
*** 2381,2386 ****
--- 2487,2493 ----
        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 24 Jun 2006 11:31:13 -0000
***************
*** 45,50 ****
--- 45,51 ----
  
  static void handleSyncLoss(PGconn *conn, char id, int msgLength);
  static int    getRowDescriptions(PGconn *conn);
+ static int    getParamDescriptions(PGconn *conn);
  static int    getAnotherTuple(PGconn *conn, int msgLength);
  static int    getParameterStatus(PGconn *conn);
  static int    getNotify(PGconn *conn);
***************
*** 81,87 ****
                if (pqGetc(&id, conn))
                        return;
                if (pqGetInt(&msgLength, 4, conn))
!                       return;
  
                /*
                 * Try to validate message type/length here.  A length less 
than 4 is
--- 82,88 ----
                if (pqGetc(&id, conn))
                        return;
                if (pqGetInt(&msgLength, 4, conn))
!                       return; 
  
                /*
                 * Try to validate message type/length here.  A length less 
than 4 is
***************
*** 206,211 ****
--- 207,213 ----
                                        }
                                        strncpy(conn->result->cmdStatus, 
conn->workBuffer.data,
                                                        CMDSTATUS_LEN);
+ 
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case 'E':               /* error return */
***************
*** 216,221 ****
--- 218,236 ----
                                case 'Z':               /* backend is ready for 
new query */
                                        if (getReadyForQuery(conn))
                                                return;
+                                       
+                                       /* 
+                                        * If there's any previosly available 
result and this is
+                                        * received from a Describe query's 
response, consume it
+                                        * first.
+                                        */
+                                       if (conn->result && conn->queryclass == 
PGQUERY_DESCRIBE)
+                                       {
+                                               conn->asyncStatus = 
PGASYNC_READY;
+                                               return;
+                                       }
+                                       
+                                       /* Backend is ready for a new query. */
                                        conn->asyncStatus = PGASYNC_IDLE;
                                        break;
                                case 'I':               /* empty query */
***************
*** 294,299 ****
--- 309,338 ----
                                                conn->result = 
PQmakeEmptyPGresult(conn,
                                                                                
                                   PGRES_COMMAND_OK);
                                        break;
+                               case 't':               /* Parameter 
Description */
+                                       /* Parse after a pqDescribe call only. 
*/
+                                       if (conn->queryclass == 
PGQUERY_DESCRIBE)
+                                       {
+                                               if (!conn->result)
+                                               {
+                                                       /* A new PGresult will 
be created to load parsed data into. */
+                                                       if 
(getParamDescriptions(conn))
+                                                               return;
+                                                       
+                                                       /* Result is ready. */
+                                                       conn->asyncStatus = 
PGASYNC_READY;
+                                               }
+                                               else
+                                               {
+                                                       /*
+                                                        * We'll wait until the 
application accepts the current
+                                                        * existing result.
+                                                        */
+                                                       conn->asyncStatus = 
PGASYNC_READY;
+                                                       return;
+                                               }
+                                       }
+                                       break;
                                case 'D':               /* Data Row */
                                        if (conn->result != NULL &&
                                                conn->result->resultStatus == 
PGRES_TUPLES_OK)
***************
*** 500,505 ****
--- 539,611 ----
  }
  
  /*
+  * parseInput subroutine to read a 't' (ParameterDescription) message.
+  * Returns 0 if parsing is completed, 1 if there isn't enough data yet
+  * and EOF if an error occurs.
+  *
+  * Parsed data will get loaded into the existing conn->result.
+  */
+ 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;
+       }
+ 
+       /* First extracting number of attributes from the message. */
+       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;
+               }
+               
+               /* Reset attribute values. */
+               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:
+       if (res)
+               PQclear(res);
+       return EOF;
+ }
+ 
+ /*
   * parseInput subroutine to read a 'D' (row data) message.
   * We add another tuple to the existing PGresult structure.
   * Returns: 0 if completed message, EOF if error or not enough data yet.
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.129
diff -c -r1.129 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h     23 May 2006 22:13:19 -0000      1.129
--- src/interfaces/libpq/libpq-fe.h     24 Jun 2006 11:31:14 -0000
***************
*** 407,412 ****
--- 407,416 ----
  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 and portals. */
+ extern int    PQdescribePrepared(PGconn *conn, const char *stmt);
+ extern int    PQdescribePortal(PGconn *conn, const char *portal);
+ 
  /* Delete a PGresult */
  extern void PQclear(PGresult *res);
  
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.113
diff -c -r1.113 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    21 May 2006 20:19:23 -0000      1.113
--- src/interfaces/libpq/libpq-int.h    24 Jun 2006 11:31:14 -0000
***************
*** 193,199 ****
  {
        PGQUERY_SIMPLE,                         /* simple Query protocol 
(PQexec) */
        PGQUERY_EXTENDED,                       /* full Extended protocol 
(PQexecParams) */
!       PGQUERY_PREPARE                         /* Parse only (PQprepare) */
  } PGQueryClass;
  
  /* PGSetenvStatusType defines the state of the PQSetenv state machine */
--- 193,200 ----
  {
        PGQUERY_SIMPLE,                         /* simple Query protocol 
(PQexec) */
        PGQUERY_EXTENDED,                       /* full Extended protocol 
(PQexecParams) */
!       PGQUERY_PREPARE,                        /* Parse only (PQprepare) */
!       PGQUERY_DESCRIBE                        /* Parse only (pqDescribe) */
  } PGQueryClass;
  
  /* PGSetenvStatusType defines the state of the PQSetenv state machine */
---------------------------(end of broadcast)---------------------------
TIP 5: don't forget to increase your free space map settings

Reply via email to