libpq_desc_msg.patch.4
text/plain
Filename: libpq_desc_msg.patch.4
Type: text/plain
Part: 0
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.483
diff -c -r1.483 postgres.c
*** src/backend/tcop/postgres.c 4 Apr 2006 19:35:35 -0000 1.483
--- src/backend/tcop/postgres.c 15 Apr 2006 07:39:49 -0000
***************
*** 1870,1875 ****
--- 1870,1876 ----
static void
exec_describe_statement_message(const char *stmt_name)
{
+ MemoryContext oldContext;
PreparedStatement *pstmt;
TupleDesc tupdesc;
ListCell *l;
***************
*** 1882,1888 ****
start_xact_command();
/* Switch back to message context */
! MemoryContextSwitchTo(MessageContext);
/* Find prepared statement */
if (stmt_name[0] != '\0')
--- 1883,1889 ----
start_xact_command();
/* Switch back to message context */
! oldContext = MemoryContextSwitchTo(MessageContext);
/* Find prepared statement */
if (stmt_name[0] != '\0')
***************
*** 1940,1946 ****
--- 1941,1950 ----
NULL);
else
pq_putemptymessage('n'); /* NoData */
+
+ MemoryContextSwitchTo(oldContext);
+ finish_xact_command();
}
/*
***************
*** 1951,1956 ****
--- 1955,1961 ----
static void
exec_describe_portal_message(const char *portal_name)
{
+ MemoryContext oldContext;
Portal portal;
/*
***************
*** 1960,1966 ****
start_xact_command();
/* Switch back to message context */
! MemoryContextSwitchTo(MessageContext);
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
--- 1965,1971 ----
start_xact_command();
/* Switch back to message context */
! oldContext = MemoryContextSwitchTo(MessageContext);
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
***************
*** 1992,1997 ****
--- 1997,2006 ----
portal->formats);
else
pq_putemptymessage('n'); /* NoData */
+
+ MemoryContextSwitchTo(oldContext);
+
+ finish_xact_command();
}
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 15 Apr 2006 07:39:58 -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,2392 ----
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 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 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
***************
*** 2346,2351 ****
--- 2452,2458 ----
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 15 Apr 2006 07:40:05 -0000
***************
*** 42,50 ****
--- 42,56 ----
((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \
(id) == 'E' || (id) == 'N' || (id) == 'A')
+ /*
+ * Flag to distinguish between an odd RowDescription and start of
+ * a tuple returning query.
+ */
+ static bool parsing_row_desc = false;
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);
***************
*** 206,221 ****
--- 212,247 ----
}
strncpy(conn->result->cmdStatus, conn->workBuffer.data,
CMDSTATUS_LEN);
+
+ /* This cannot be a Describe ('D') response no more. */
+ parsing_row_desc = false;
+
conn->asyncStatus = PGASYNC_READY;
break;
case 'E': /* error return */
if (pqGetErrorNotice3(conn, true))
return;
conn->asyncStatus = PGASYNC_READY;
+
+ /* No further RowDescription parsing is possible. */
+ parsing_row_desc = false;
break;
case 'Z': /* backend is ready for new query */
if (getReadyForQuery(conn))
return;
+
+ if (parsing_row_desc)
+ {
+ /*
+ * This is probably received from a Describe ('D')
+ * query's response.
+ */
+ parsing_row_desc = false;
+ conn->asyncStatus = PGASYNC_READY;
+ return;
+ }
+
+ /* Backend is ready for a new query. */
conn->asyncStatus = PGASYNC_IDLE;
break;
case 'I': /* empty query */
***************
*** 268,273 ****
--- 294,302 ----
/* First 'T' in a query sequence */
if (getRowDescriptions(conn))
return;
+
+ /* Turning "RowDesc is parsed" flag to on. */
+ parsing_row_desc = true;
}
else
{
***************
*** 294,299 ****
--- 323,348 ----
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK);
break;
+ case 't': /* Parameter Description */
+ 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 ****
--- 549,620 ----
}
/*
+ * 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"));
+ EOF;
+ }
+
+ /* 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:
+ 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.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 15 Apr 2006 07:40:08 -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 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);