-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.214 2006/07/27 13:20:24 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.215 2006/08/18 19:52:39 tgl Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
to the parameter symbol in the same way it would do for an untyped literal
string. Also, the query may use parameter symbols with numbers higher than
<parameter>nParams</>; data types will be inferred for these symbols as
-well.
+well. (See <function>PQdescribePrepared</function> for a means to find out
+what data types were inferred.)
</para>
<para>
Use <function>PQerrorMessage</function> to get more information
about such errors.
</para>
-
-<para>
-At present, there is no way to determine the actual data type inferred for
-any parameters whose types are not specified in <parameter>paramTypes[]</>.
-This is a <application>libpq</> omission that will probably be rectified
-in a future release.
-</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</listitem>
</varlistentry>
+
+<varlistentry>
+<term><function>PQdescribePrepared</function><indexterm><primary>PQdescribePrepared</></></term>
+<listitem>
+<para>
+ Submits a request to obtain information about the specified
+ prepared statement, and waits for completion.
+<synopsis>
+PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName);
+</synopsis>
+</para>
+
+<para>
+<function>PQdescribePrepared</> allows an application to obtain information
+about a previously prepared statement.
+<function>PQdescribePrepared</> is supported only in protocol 3.0 and later
+connections; it will fail when using protocol 2.0.
+</para>
+
+<para>
+<parameter>stmtName</> may be <literal>""</> or NULL to reference the unnamed
+statement, otherwise it must be the name of an existing prepared statement.
+On success, a <structname>PGresult</> with status
+<literal>PGRES_COMMAND_OK</literal> is returned. The functions
+<function>PQnparams</function> and <function>PQparamtype</function>
+may be applied to this <structname>PGresult</> to obtain information
+about the parameters of the prepared statement, and the functions
+<function>PQnfields</function>, <function>PQfname</function>,
+<function>PQftype</function>, etc provide information about the result
+columns (if any) of the statement.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQdescribePortal</function><indexterm><primary>PQdescribePortal</></></term>
+<listitem>
+<para>
+ Submits a request to obtain information about the specified
+ portal, and waits for completion.
+<synopsis>
+PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
+</synopsis>
+</para>
+
+<para>
+<function>PQdescribePortal</> allows an application to obtain information
+about a previously created portal. (<application>libpq</> does not provide
+any direct access to portals, but you can use this function to inspect the
+properties of a cursor created with a <command>DECLARE CURSOR</> SQL command.)
+<function>PQdescribePortal</> is supported only in protocol 3.0 and later
+connections; it will fail when using protocol 2.0.
+</para>
+
+<para>
+<parameter>portalName</> may be <literal>""</> or NULL to reference the unnamed
+portal, otherwise it must be the name of an existing portal.
+On success, a <structname>PGresult</> with status
+<literal>PGRES_COMMAND_OK</literal> is returned. The functions
+<function>PQnfields</function>, <function>PQfname</function>,
+<function>PQftype</function>, etc may be applied to the
+<structname>PGresult</> to obtain information about the result
+columns (if any) of the portal.
+</para>
+</listitem>
+</varlistentry>
</variablelist>
</para>
These functions are used to extract information from a
<structname>PGresult</structname> object that represents a successful
query result (that is, one that has status
-<literal>PGRES_TUPLES_OK</literal>). For objects with other status
-values they will act as though the result has zero rows and zero columns.
+<literal>PGRES_TUPLES_OK</literal>). They can also be used to extract
+information from a successful Describe operation: a Describe's result
+has all the same column information that actual execution of the query
+would provide, but it has zero rows. For objects with other status values,
+these functions will act as though the result has zero rows and zero columns.
</para>
<variablelist>
</listitem>
</varlistentry>
+<varlistentry>
+<term><function>PQnparams</function><indexterm><primary>PQnparams</></></term>
+<listitem>
+<para>
+ Returns the number of parameters of a prepared statement.
+<synopsis>
+int PQnparams(const PGresult *res);
+</synopsis>
+</para>
+
+<para>
+This function is only useful when inspecting the result of
+<function>PQdescribePrepared</>. For other types of queries it will
+return zero.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQparamtype</function><indexterm><primary>PQparamtype</></></term>
+<listitem>
+<para>
+ Returns the data type of the indicated statement parameter.
+ Parameter numbers start at 0.
+<synopsis>
+Oid PQparamtype(const PGresult *res, int param_number);
+</synopsis>
+</para>
+
+<para>
+This function is only useful when inspecting the result of
+<function>PQdescribePrepared</>. For other types of queries it will
+return zero.
+</para>
+</listitem>
+</varlistentry>
+
<varlistentry>
<term><function>PQprint</function><indexterm><primary>PQprint</></></term>
<listitem>
<function>PQsendQuery</function> and <function>PQgetResult</function>.
There are also
<function>PQsendQueryParams</function>,
-<function>PQsendPrepare</function>, and
+<function>PQsendPrepare</function>,
<function>PQsendQueryPrepared</function>,
+<function>PQsendDescribePrepared</function>, and
+<function>PQsendDescribePortal</function>,
which can be used with <function>PQgetResult</function> to duplicate the
functionality of
<function>PQexecParams</function>,
-<function>PQprepare</function>, and
-<function>PQexecPrepared</function>
+<function>PQprepare</function>,
+<function>PQexecPrepared</function>,
+<function>PQdescribePrepared</function>, and
+<function>PQdescribePortal</function>
respectively.
<variablelist>
</listitem>
</varlistentry>
+<varlistentry>
+<term><function>PQsendDescribePrepared</><indexterm><primary>PQsendDescribePrepared</></></term>
+<listitem>
+<para>
+ Submits a request to obtain information about the specified
+ prepared statement, without waiting for completion.
+<synopsis>
+int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
+</synopsis>
+
+ This is an asynchronous version of <function>PQdescribePrepared</>: it
+ returns 1 if it was able to dispatch the request, and 0 if not.
+ After a successful call, call <function>PQgetResult</function>
+ to obtain the results.
+ The function's parameters are handled identically to
+ <function>PQdescribePrepared</function>. Like
+ <function>PQdescribePrepared</function>, it will not work on 2.0-protocol
+ connections.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQsendDescribePortal</><indexterm><primary>PQsendDescribePortal</></></term>
+<listitem>
+<para>
+ Submits a request to obtain information about the specified
+ portal, without waiting for completion.
+<synopsis>
+int PQsendDescribePortal(PGconn *conn, const char *portalName);
+</synopsis>
+
+ This is an asynchronous version of <function>PQdescribePortal</>: it
+ returns 1 if it was able to dispatch the request, and 0 if not.
+ After a successful call, call <function>PQgetResult</function>
+ to obtain the results.
+ The function's parameters are handled identically to
+ <function>PQdescribePortal</function>. Like
+ <function>PQdescribePortal</function>, it will not work on 2.0-protocol
+ connections.
+</para>
+</listitem>
+</varlistentry>
+
<varlistentry>
<term><function>PQgetResult</function><indexterm><primary>PQgetResult</></></term>
<listitem>
-# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.13 2006/07/04 13:22:15 momjian Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.14 2006/08/18 19:52:39 tgl Exp $
# Functions to be exported by libpq DLLs
PQconnectdb 1
PQsetdbLogin 2
PQencryptPassword 128
PQisthreadsafe 129
enlargePQExpBuffer 130
+PQnparams 131
+PQparamtype 132
+PQdescribePrepared 133
+PQdescribePortal 134
+PQsendDescribePrepared 135
+PQsendDescribePortal 136
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.189 2006/08/04 22:20:06 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.190 2006/08/18 19:52:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void parseInput(PGconn *conn);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
+static int PQsendDescribe(PGconn *conn, char desc_type,
+ const char *desc_target);
/* ----------------
result->attDescs = NULL;
result->tuples = NULL;
result->tupArrSize = 0;
+ result->numParameters = 0;
+ result->paramDescs = NULL;
result->resultStatus = status;
result->cmdStatus[0] = '\0';
result->binary = 0;
/* zero out the pointer fields to catch programming errors */
res->attDescs = NULL;
res->tuples = NULL;
+ res->paramDescs = NULL;
res->errFields = NULL;
/* res->curBlock was zeroed out earlier */
return lastResult;
}
+/*
+ * PQdescribePrepared
+ * Obtain information about a previously prepared statement
+ *
+ * If the query was not even sent, return NULL; conn->errorMessage is set to
+ * a relevant message.
+ * If the query was sent, a new PGresult is returned (which could indicate
+ * either success or failure). On success, the PGresult contains status
+ * PGRES_COMMAND_OK, and its parameter and column-heading fields describe
+ * the statement's inputs and outputs respectively.
+ * The user is responsible for freeing the PGresult via PQclear()
+ * when done with it.
+ */
+PGresult *
+PQdescribePrepared(PGconn *conn, const char *stmt)
+{
+ if (!PQexecStart(conn))
+ return NULL;
+ if (!PQsendDescribe(conn, 'S', stmt))
+ return NULL;
+ return PQexecFinish(conn);
+}
+
+/*
+ * PQdescribePortal
+ * Obtain information about a previously created portal
+ *
+ * This is much like PQdescribePrepared, except that no parameter info is
+ * returned. Note that at the moment, libpq doesn't really expose portals
+ * to the client; but this can be used with a portal created by a SQL
+ * DECLARE CURSOR command.
+ */
+PGresult *
+PQdescribePortal(PGconn *conn, const char *portal)
+{
+ if (!PQexecStart(conn))
+ return NULL;
+ if (!PQsendDescribe(conn, 'P', portal))
+ return NULL;
+ return PQexecFinish(conn);
+}
+
+/*
+ * PQsendDescribePrepared
+ * Submit a Describe Statement command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ * 0 if error (conn->errorMessage is set)
+ */
+int
+PQsendDescribePrepared(PGconn *conn, const char *stmt)
+{
+ return PQsendDescribe(conn, 'S', stmt);
+}
+
+/*
+ * PQsendDescribePortal
+ * Submit a Describe Portal command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ * 0 if error (conn->errorMessage is set)
+ */
+int
+PQsendDescribePortal(PGconn *conn, const char *portal)
+{
+ return PQsendDescribe(conn, 'P', portal);
+}
+
+/*
+ * PQsendDescribe
+ * Common code to send a Describe command
+ *
+ * Available options for desc_type are
+ * 'S' to describe a prepared statement; or
+ * 'P' to describe a portal.
+ * Returns 1 on success and 0 on failure.
+ */
+static int
+PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
+{
+ /* Treat null desc_target as empty string */
+ if (!desc_target)
+ desc_target = "";
+
+ if (!PQsendQueryStart(conn))
+ return 0;
+
+ /* 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;
+ }
+
+ /* construct the Describe message */
+ if (pqPutMsgStart('D', false, conn) < 0 ||
+ pqPutc(desc_type, conn) < 0 ||
+ pqPuts(desc_target, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /* construct the Sync message */
+ if (pqPutMsgStart('S', false, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /* remember we are doing a Describe */
+ conn->queryclass = PGQUERY_DESCRIBE;
+
+ /* reset last-query string (not relevant now) */
+ if (conn->last_query)
+ {
+ free(conn->last_query);
+ conn->last_query = NULL;
+ }
+
+ /*
+ * Give the data a push. In nonblock mode, don't complain if we're unable
+ * to send it all; PQgetResult() will do any additional flushing needed.
+ */
+ if (pqFlush(conn) < 0)
+ goto sendFailed;
+
+ /* OK, it's launched! */
+ conn->asyncStatus = PGASYNC_BUSY;
+ return 1;
+
+sendFailed:
+ pqHandleSendFailure(conn);
+ return 0;
+}
+
/*
* PQnotifies
* returns a PGnotify* structure of the latest async notification
return TRUE;
}
+static int
+check_param_number(const PGresult *res, int param_num)
+{
+ if (!res)
+ return FALSE; /* no way to display error message... */
+ if (param_num < 0 || param_num >= res->numParameters)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "parameter number %d is out of range 0..%d",
+ param_num, res->numParameters - 1);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/*
* returns NULL if the field_num is invalid
*/
return 0;
}
+/* PQnparams:
+ * returns the number of input parameters of a prepared statement.
+ */
+int
+PQnparams(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->numParameters;
+}
+
+/* PQparamtype:
+ * returns type Oid of the specified statement parameter.
+ */
+Oid
+PQparamtype(const PGresult *res, int param_num)
+{
+ if (!check_param_number(res, param_num))
+ return InvalidOid;
+ if (res->paramDescs)
+ return res->paramDescs[param_num].typid;
+ else
+ return InvalidOid;
+}
+
+
/* 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
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.26 2006/03/14 22:48:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.27 2006/08/18 19:52:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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);
return;
break;
case 'T': /* Row Description */
- if (conn->result == NULL)
+ if (conn->result == NULL ||
+ conn->queryclass == PGQUERY_DESCRIBE)
{
/* First 'T' in a query sequence */
if (getRowDescriptions(conn))
return;
+ /*
+ * If we're doing a Describe, we're ready to pass
+ * the result back to the client.
+ */
+ if (conn->queryclass == PGQUERY_DESCRIBE)
+ conn->asyncStatus = PGASYNC_READY;
}
else
{
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK);
+ /*
+ * If we're doing a Describe, we're ready to pass
+ * the result back to the client.
+ */
+ if (conn->queryclass == PGQUERY_DESCRIBE)
+ conn->asyncStatus = PGASYNC_READY;
+ break;
+ case 't': /* Parameter Description */
+ if (getParamDescriptions(conn))
+ return;
break;
case 'D': /* Data Row */
if (conn->result != NULL &&
/*
* parseInput subroutine to read a 'T' (row descriptions) message.
- * We build a PGresult structure containing the attribute data.
+ * We'll build a new PGresult structure (unless called for a Describe
+ * command for a prepared statement) containing the attribute data.
* Returns: 0 if completed message, EOF if not enough data yet.
*
* Note that if we run out of data, we have to release the partially
int nfields;
int i;
- result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
+ /*
+ * When doing Describe for a prepared statement, there'll already be
+ * a PGresult created by getParamDescriptions, and we should fill
+ * data into that. Otherwise, create a new, empty PGresult.
+ */
+ if (conn->queryclass == PGQUERY_DESCRIBE)
+ {
+ if (conn->result)
+ result = conn->result;
+ else
+ result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
+ }
+ else
+ result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
if (!result)
goto failure;
/* parseInput already read the 'T' label and message length. */
- /* the next two bytes are the number of fields */
+ /* the next two bytes are the number of fields */
if (pqGetInt(&(result->numAttributes), 2, conn))
goto failure;
nfields = result->numAttributes;
conn->result = result;
return 0;
+failure:
+ /*
+ * Discard incomplete result, unless it's from getParamDescriptions.
+ *
+ * Note that if we hit a bufferload boundary while handling the
+ * describe-statement case, we'll forget any PGresult space we just
+ * allocated, and then reallocate it on next try. This will bloat
+ * the PGresult a little bit but the space will be freed at PQclear,
+ * so it doesn't seem worth trying to be smarter.
+ */
+ if (result != conn->result)
+ PQclear(result);
+ return EOF;
+}
+
+/*
+ * parseInput subroutine to read a 't' (ParameterDescription) message.
+ * We'll build a new PGresult structure containing the parameter data.
+ * Returns: 0 if completed message, EOF if not enough data yet.
+ *
+ * Note that if we run out of data, we have to release the partially
+ * constructed PGresult, and rebuild it again next time. Fortunately,
+ * that shouldn't happen often, since 't' messages usually fit in a packet.
+ */
+static int
+getParamDescriptions(PGconn *conn)
+{
+ PGresult *result;
+ int nparams;
+ int i;
+
+ result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
+ if (!result)
+ goto failure;
+
+ /* parseInput already read the 't' label and message length. */
+ /* the next two bytes are the number of parameters */
+ if (pqGetInt(&(result->numParameters), 2, conn))
+ goto failure;
+ nparams = result->numParameters;
+
+ /* allocate space for the parameter descriptors */
+ if (nparams > 0)
+ {
+ result->paramDescs = (PGresParamDesc *)
+ pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE);
+ if (!result->paramDescs)
+ goto failure;
+ MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
+ }
+
+ /* get parameter info */
+ for (i = 0; i < nparams; i++)
+ {
+ int typid;
+
+ if (pqGetInt(&typid, 4, conn))
+ goto failure;
+ result->paramDescs[i].typid = typid;
+ }
+
+ /* Success! */
+ conn->result = result;
+ return 0;
+
failure:
PQclear(result);
return EOF;
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.131 2006/07/04 13:22:15 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.132 2006/08/18 19:52:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num);
extern int PQgetlength(const PGresult *res, int tup_num, int field_num);
extern int PQgetisnull(const PGresult *res, int tup_num, int field_num);
+extern int PQnparams(const PGresult *res);
+extern Oid PQparamtype(const PGresult *res, int param_num);
+
+/* Describe prepared statements and portals */
+extern PGresult *PQdescribePrepared(PGconn *conn, const char *stmt);
+extern PGresult *PQdescribePortal(PGconn *conn, const char *portal);
+extern int PQsendDescribePrepared(PGconn *conn, const char *stmt);
+extern int PQsendDescribePortal(PGconn *conn, const char *portal);
/* Delete a PGresult */
extern void PQclear(PGresult *res);
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.114 2006/08/04 18:58:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.115 2006/08/18 19:52:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int atttypmod; /* type-specific modifier info */
} PGresAttDesc;
+/* Data about a single parameter of a prepared statement */
+typedef struct pgresParamDesc
+{
+ Oid typid; /* type id */
+} PGresParamDesc;
+
/*
* Data for a single attribute of a single tuple
*
PGresAttValue **tuples; /* each PGresTuple is an array of
* PGresAttValue's */
int tupArrSize; /* allocated size of tuples array */
+ int numParameters;
+ PGresParamDesc *paramDescs;
ExecStatusType resultStatus;
char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the query */
int binary; /* binary tuple values if binary == 1,
{
PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */
PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */
- PGQUERY_PREPARE /* Parse only (PQprepare) */
+ PGQUERY_PREPARE, /* Parse only (PQprepare) */
+ PGQUERY_DESCRIBE /* Describe Statement or Portal */
} PGQueryClass;
/* PGSetenvStatusType defines the state of the PQSetenv state machine */