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 */