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