1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-12 15:23:02 +03:00

Add PQdescribePrepared, PQdescribePortal, and related functions to libpq

to allow obtaining information about previously prepared statements and
open cursors.  Volkan Yazici
This commit is contained in:
Tom Lane
2006-08-18 19:52:39 +00:00
parent 46d61eb218
commit 9a8920e1d7
6 changed files with 472 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
# $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
@@ -130,3 +130,9 @@ PQescapeByteaConn 127
PQencryptPassword 128
PQisthreadsafe 129
enlargePQExpBuffer 130
PQnparams 131
PQparamtype 132
PQdescribePrepared 133
PQdescribePortal 134
PQsendDescribePrepared 135
PQsendDescribePortal 136

View File

@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -61,6 +61,8 @@ static int PQsendQueryGuts(PGconn *conn,
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);
/* ----------------
@@ -147,6 +149,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
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;
@@ -367,6 +371,7 @@ PQclear(PGresult *res)
/* 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 */
@@ -1471,6 +1476,139 @@ PQexecFinish(PGconn *conn)
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
@@ -1984,6 +2122,22 @@ check_tuple_field_number(const PGresult *res,
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
*/
@@ -2307,6 +2461,32 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
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

View File

@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -45,6 +45,7 @@
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);
@@ -263,11 +264,18 @@ pqParseInput3(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
{
@@ -293,6 +301,16 @@ pqParseInput3(PGconn *conn)
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 &&
@@ -409,7 +427,8 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
/*
* 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
@@ -423,12 +442,25 @@ getRowDescriptions(PGconn *conn)
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;
@@ -494,6 +526,71 @@ getRowDescriptions(PGconn *conn)
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;

View File

@@ -7,7 +7,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -406,6 +406,14 @@ extern char *PQcmdTuples(PGresult *res);
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);

View File

@@ -12,7 +12,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -94,6 +94,12 @@ typedef struct pgresAttDesc
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
*
@@ -145,6 +151,8 @@ struct pg_result
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,
@@ -193,7 +201,8 @@ typedef enum
{
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 */