* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.70 2003/05/06 20:26:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.71 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/lsyscache.h"
+#include "utils/portal.h"
static void printtup_startup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist);
-static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
-static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
+ TupleDesc typeinfo);
+static void printtup(HeapTuple tuple, TupleDesc typeinfo,
+ DestReceiver *self);
+static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
+ DestReceiver *self);
+static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
+ DestReceiver *self);
static void printtup_shutdown(DestReceiver *self);
static void printtup_destroy(DestReceiver *self);
typedef struct
{
DestReceiver pub; /* publicly-known function pointers */
+ Portal portal; /* the Portal we are printing from */
bool sendDescrip; /* send RowDescription at startup? */
TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs;
* ----------------
*/
DestReceiver *
-printtup_create_DR(CommandDest dest)
+printtup_create_DR(CommandDest dest, Portal portal)
{
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
- bool isBinary;
- bool sendDescrip;
- switch (dest)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ self->pub.receiveTuple = printtup;
+ else
{
- case Remote:
- isBinary = false;
- sendDescrip = true;
- break;
- case RemoteInternal:
- isBinary = true;
- sendDescrip = true;
- break;
- case RemoteExecute:
- isBinary = false;
- sendDescrip = false; /* no T message for Execute */
- break;
- case RemoteExecuteInternal:
- isBinary = true;
- sendDescrip = false; /* no T message for Execute */
- break;
-
- default:
- elog(ERROR, "printtup_create_DR: unsupported dest");
- return NULL;
+ /*
+ * In protocol 2.0 the Bind message does not exist, so there is
+ * no way for the columns to have different print formats; it's
+ * sufficient to look at the first one.
+ */
+ if (portal->formats && portal->formats[0] != 0)
+ self->pub.receiveTuple = printtup_internal_20;
+ else
+ self->pub.receiveTuple = printtup_20;
}
-
- self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
self->pub.startup = printtup_startup;
self->pub.shutdown = printtup_shutdown;
self->pub.destroy = printtup_destroy;
self->pub.mydest = dest;
- self->sendDescrip = sendDescrip;
+ self->portal = portal;
+
+ /* Send T message automatically if Remote, but not if RemoteExecute */
+ self->sendDescrip = (dest == Remote);
self->attrinfo = NULL;
self->nattrs = 0;
}
static void
-printtup_startup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist)
+printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
DR_printtup *myState = (DR_printtup *) self;
+ Portal portal = myState->portal;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
*
* If portal name not specified, use "blank" portal.
*/
- if (portalName == NULL)
+ const char *portalName = portal->name;
+
+ if (portalName == NULL || portalName[0] == '\0')
portalName = "blank";
pq_puttextmessage('P', portalName);
* then we send back the tuple descriptor of the tuples.
*/
if (operation == CMD_SELECT && myState->sendDescrip)
- SendRowDescriptionMessage(typeinfo, targetlist);
+ {
+ List *targetlist;
+
+ if (portal->strategy == PORTAL_ONE_SELECT)
+ targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
+ else
+ targetlist = NIL;
+
+ SendRowDescriptionMessage(typeinfo, targetlist, portal->formats);
+ }
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
* Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
* or some similar function; it does not contain a full set of fields.
* The targetlist will be NIL when executing a utility function that does
- * not have a plan. If the targetlist isn't NIL then it is a Plan node's
- * targetlist; it is up to us to ignore resjunk columns in it.
+ * not have a plan. If the targetlist isn't NIL then it is a Query node's
+ * targetlist; it is up to us to ignore resjunk columns in it. The formats[]
+ * array pointer might be NULL (if we are doing Describe on a prepared stmt);
+ * send zeroes for the format codes in that case.
*/
void
-SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
{
Form_pg_attribute *attrs = typeinfo->attrs;
int natts = typeinfo->natts;
if (proto >= 2)
pq_sendint(&buf, attrs[i]->atttypmod,
sizeof(attrs[i]->atttypmod));
+ /* format info appears in protocol 3.0 and up */
+ if (proto >= 3)
+ {
+ if (formats)
+ pq_sendint(&buf, formats[i], 2);
+ else
+ pq_sendint(&buf, 0, 2);
+ }
}
pq_endmessage(&buf);
}
}
/* ----------------
- * printtup
+ * printtup --- print a tuple in protocol 3.0
* ----------------
*/
static void
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+{
+ DR_printtup *myState = (DR_printtup *) self;
+ int16 *formats = myState->portal->formats;
+ StringInfoData buf;
+ int natts = tuple->t_data->t_natts;
+ int i;
+
+ /* Set or update my derived attribute info, if needed */
+ if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+ printtup_prepare_info(myState, typeinfo, natts);
+
+ /*
+ * Prepare a DataRow message
+ */
+ pq_beginmessage(&buf, 'D');
+
+ pq_sendint(&buf, natts, 2);
+
+ /*
+ * send the attributes of this tuple
+ */
+ for (i = 0; i < natts; ++i)
+ {
+ PrinttupAttrInfo *thisState = myState->myinfo + i;
+ int16 format = (formats ? formats[i] : 0);
+ Datum origattr,
+ attr;
+ bool isnull;
+ char *outputstr;
+
+ origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+ if (isnull)
+ {
+ pq_sendint(&buf, -1, 4);
+ continue;
+ }
+ if (format == 0)
+ {
+ if (OidIsValid(thisState->typoutput))
+ {
+ /*
+ * If we have a toasted datum, forcibly detoast it here to
+ * avoid memory leakage inside the type's output routine.
+ */
+ if (thisState->typisvarlena)
+ attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+ else
+ attr = origattr;
+
+ outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
+ attr,
+ ObjectIdGetDatum(thisState->typelem),
+ Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+
+ pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+
+ /* Clean up detoasted copy, if any */
+ if (attr != origattr)
+ pfree(DatumGetPointer(attr));
+ pfree(outputstr);
+ }
+ else
+ {
+ outputstr = "<unprintable>";
+ pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+ }
+ }
+ else if (format == 1)
+ {
+ /* XXX something similar to above */
+ elog(ERROR, "Binary transmission not implemented yet");
+ }
+ else
+ {
+ elog(ERROR, "Invalid format code %d", format);
+ }
+ }
+
+ pq_endmessage(&buf);
+}
+
+/* ----------------
+ * printtup_20 --- print a tuple in protocol 2.0
+ * ----------------
+ */
+static void
+printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf;
ObjectIdGetDatum(thisState->typelem),
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
- pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+ pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
/* Clean up detoasted copy, if any */
if (attr != origattr)
else
{
outputstr = "<unprintable>";
- pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+ pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
}
}
attributeP->attbyval ? 't' : 'f');
}
-/* ----------------
- * showatts
- * ----------------
- */
-static void
-showatts(const char *name, TupleDesc tupleDesc)
-{
- int natts = tupleDesc->natts;
- Form_pg_attribute *attinfo = tupleDesc->attrs;
- int i;
-
- puts(name);
- for (i = 0; i < natts; ++i)
- printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
- printf("\t----\n");
-}
-
/* ----------------
* debugStartup - prepare to print tuples for an interactive backend
* ----------------
*/
void
-debugStartup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist)
+debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
+ int natts = typeinfo->natts;
+ Form_pg_attribute *attinfo = typeinfo->attrs;
+ int i;
+
/*
* show the return type of the tuples
*/
- if (portalName == NULL)
- portalName = "blank";
-
- showatts(portalName, typeinfo);
+ for (i = 0; i < natts; ++i)
+ printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
+ printf("\t----\n");
}
/* ----------------
}
/* ----------------
- * printtup_internal
- * We use a different data prefix, e.g. 'B' instead of 'D' to
- * indicate a tuple in internal (binary) form.
+ * printtup_internal_20 --- print a binary tuple in protocol 2.0
+ *
+ * We use a different message type, i.e. 'B' instead of 'D' to
+ * indicate a tuple in internal (binary) form.
*
- * This is largely same as printtup, except we don't use the typout func.
+ * This is largely same as printtup_20, except we don't use the typout func.
* ----------------
*/
static void
-printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.197 2003/04/25 02:28:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.198 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Internal communications functions
*/
-static void SendCopyBegin(bool binary);
-static void ReceiveCopyBegin(bool binary);
+static void SendCopyBegin(bool binary, int natts);
+static void ReceiveCopyBegin(bool binary, int natts);
static void SendCopyEnd(bool binary);
static void CopySendData(void *databuf, int datasize);
static void CopySendString(const char *str);
* in past protocol redesigns.
*/
static void
-SendCopyBegin(bool binary)
+SendCopyBegin(bool binary, int natts)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
/* new way */
StringInfoData buf;
+ int16 format = (binary ? 1 : 0);
+ int i;
pq_beginmessage(&buf, 'H');
- pq_sendbyte(&buf, binary ? 1 : 0);
+ pq_sendbyte(&buf, format); /* overall format */
+ pq_sendint(&buf, natts, 2);
+ for (i = 0; i < natts; i++)
+ pq_sendint(&buf, format, 2); /* per-column formats */
pq_endmessage(&buf);
copy_dest = COPY_NEW_FE;
copy_msgbuf = makeStringInfo();
}
static void
-ReceiveCopyBegin(bool binary)
+ReceiveCopyBegin(bool binary, int natts)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
/* new way */
StringInfoData buf;
+ int16 format = (binary ? 1 : 0);
+ int i;
pq_beginmessage(&buf, 'G');
- pq_sendbyte(&buf, binary ? 1 : 0);
+ pq_sendbyte(&buf, format); /* overall format */
+ pq_sendint(&buf, natts, 2);
+ for (i = 0; i < natts; i++)
+ pq_sendint(&buf, format, 2); /* per-column formats */
pq_endmessage(&buf);
copy_dest = COPY_NEW_FE;
copy_msgbuf = makeStringInfo();
if (pipe)
{
if (IsUnderPostmaster)
- ReceiveCopyBegin(binary);
+ ReceiveCopyBegin(binary, length(attnumlist));
else
copy_file = stdin;
}
if (pipe)
{
if (IsUnderPostmaster)
- SendCopyBegin(binary);
+ SendCopyBegin(binary, length(attnumlist));
else
copy_file = stdout;
}
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.108 2003/05/06 20:26:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.109 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
plan = planner(query, isCursor, cursorOptions);
/* Create a QueryDesc requesting no output */
- queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, NULL,
+ queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
stmt->analyze);
ExplainOnePlan(queryDesc, stmt, tstate);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.15 2003/05/06 20:26:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.16 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "commands/portalcmds.h"
#include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
#include "optimizer/planner.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
DestReceiver *dest,
char *completionTag)
{
- DestReceiver *mydest = dest;
Portal portal;
long nprocessed;
return;
}
- /*
- * Adjust dest if needed. MOVE wants destination None.
- *
- * If fetching from a binary cursor and the requested destination is
- * Remote, change it to RemoteInternal. Note we do NOT change if the
- * destination is RemoteExecute --- so the Execute message's format
- * specification wins out over the cursor's type.
- */
+ /* Adjust dest if needed. MOVE wants destination None */
if (stmt->ismove)
- mydest = CreateDestReceiver(None);
- else if (dest->mydest == Remote &&
- (portal->cursorOptions & CURSOR_OPT_BINARY))
- mydest = CreateDestReceiver(RemoteInternal);
+ dest = None_Receiver;
/* Do it */
nprocessed = PortalRunFetch(portal,
stmt->direction,
stmt->howMany,
- mydest);
+ dest);
/* Return command status if wanted */
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
stmt->ismove ? "MOVE" : "FETCH",
nprocessed);
-
- /* Clean up if we created a local destination */
- if (mydest != dest)
- (mydest->destroy) (mydest);
}
/*
ExecutorRewind(queryDesc);
/* Change the destination to output to the tuplestore */
- queryDesc->dest = CreateTuplestoreDestReceiver(portal->holdStore,
- portal->holdContext);
+ queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
/* Fetch the result set into the tuplestore */
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.17 2003/05/06 21:51:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.18 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* Create a QueryDesc requesting no output */
- qdesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
+ qdesc = CreateQueryDesc(query, plan, None_Receiver,
paramLI, stmt->analyze);
ExplainOnePlan(qdesc, stmt, tstate);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.208 2003/05/06 20:26:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.209 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
- (*dest->startup) (dest, operation,
- queryDesc->portalName,
- queryDesc->tupDesc,
- queryDesc->planstate->plan->targetlist);
+ (*dest->startup) (dest, operation, queryDesc->tupDesc);
/*
* run plan
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.65 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.66 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
tstate->dest = dest;
- (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT,
- NULL, tupdesc, NIL);
+ (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, tupdesc);
return tstate;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.64 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.65 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Assert(es->qd == NULL);
es->qd = CreateQueryDesc(es->query, es->plan,
- None_Receiver, NULL,
+ None_Receiver,
fcache->paramLI, false);
/* Utility commands don't need Executor. */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.96 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.97 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
void
SPI_cursor_fetch(Portal portal, bool forward, int count)
{
- _SPI_cursor_operation(portal, forward, count, CreateDestReceiver(SPI));
+ _SPI_cursor_operation(portal, forward, count,
+ CreateDestReceiver(SPI, NULL));
/* we know that the SPI receiver doesn't need a destroy call */
}
* of current SPI procedure
*/
void
-spi_dest_startup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist)
+spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
planTree = pg_plan_query(queryTree);
plan_list = lappend(plan_list, planTree);
- dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+ dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
if (queryTree->commandType == CMD_UTILITY)
{
if (IsA(queryTree->utilityStmt, CopyStmt))
else if (plan == NULL)
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
- NULL, NULL, false);
+ NULL, false);
res = _SPI_pquery(qdesc, true,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
else
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
- NULL, NULL, false);
+ NULL, false);
res = _SPI_pquery(qdesc, false, 0);
if (res < 0)
return res;
planTree = lfirst(plan_list);
plan_list = lnext(plan_list);
- dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+ dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
if (queryTree->commandType == CMD_UTILITY)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
else
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
- NULL, paramLI, false);
+ paramLI, false);
res = _SPI_pquery(qdesc, true,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.5 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.6 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Prepare to receive tuples from executor.
*/
static void
-tstoreStartupReceiver(DestReceiver *self, int operation,
- const char *portalname,
- TupleDesc typeinfo, List *targetlist)
+tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
{
/* do nothing */
}
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.29 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* pq_sendcountedtext - append a text string (with character set conversion)
*
* The data sent to the frontend by this routine is a 4-byte count field
- * (the count includes itself, by convention) followed by the string.
+ * followed by the string. The count includes itself or not, as per the
+ * countincludesself flag (pre-3.0 protocol requires it to include itself).
* The passed text string need not be null-terminated, and the data sent
* to the frontend isn't either.
* --------------------------------
*/
void
-pq_sendcountedtext(StringInfo buf, const char *str, int slen)
+pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+ bool countincludesself)
{
+ int extra = countincludesself ? 4 : 0;
char *p;
p = (char *) pg_server_to_client((unsigned char *) str, slen);
if (p != str) /* actual conversion has been done? */
{
slen = strlen(p);
- pq_sendint(buf, slen + 4, 4);
+ pq_sendint(buf, slen + extra, 4);
appendBinaryStringInfo(buf, p, slen);
pfree(p);
- return;
}
- pq_sendint(buf, slen + 4, 4);
- appendBinaryStringInfo(buf, str, slen);
+ else
+ {
+ pq_sendint(buf, slen + extra, 4);
+ appendBinaryStringInfo(buf, str, slen);
+ }
}
/* --------------------------------
{
const char *result;
- if (datalen > (msg->len - msg->cursor))
+ if (datalen < 0 || datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
result = &msg->data[msg->cursor];
msg->cursor += datalen;
void
pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
{
- if (datalen > (msg->len - msg->cursor))
+ if (datalen < 0 || datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
memcpy(buf, &msg->data[msg->cursor], datalen);
msg->cursor += datalen;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.57 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.58 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/printtup.h"
#include "access/xact.h"
+#include "executor/tstoreReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+#include "utils/portal.h"
/* ----------------
}
static void
-donothingStartup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist)
+donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
}
/* ----------------
* CreateDestReceiver - return appropriate receiver function set for dest
+ *
+ * Note: a Portal must be specified for destinations Remote, RemoteExecute,
+ * and Tuplestore. It can be NULL for the others.
* ----------------
*/
DestReceiver *
-CreateDestReceiver(CommandDest dest)
+CreateDestReceiver(CommandDest dest, Portal portal)
{
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
- return printtup_create_DR(dest);
+ if (portal == NULL)
+ elog(ERROR, "CreateDestReceiver: no portal specified");
+ return printtup_create_DR(dest, portal);
case None:
return &donothingDR;
return &spi_printtupDR;
case Tuplestore:
- /*
- * This is disallowed, you must use tstoreReceiver.c's
- * specialized function to create a Tuplestore DestReceiver
- */
- elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore");
- break;
+ if (portal == NULL)
+ elog(ERROR, "CreateDestReceiver: no portal specified");
+ if (portal->holdStore == NULL ||
+ portal->holdContext == NULL)
+ elog(ERROR, "CreateDestReceiver: portal has no holdStore");
+ return CreateTuplestoreDestReceiver(portal->holdStore,
+ portal->holdContext);
}
/* should never get here */
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
pq_puttextmessage('C', commandTag);
break;
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
/*
* tell the fe that we saw an empty query string. In protocols
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
StringInfoData buf;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
#include "utils/tqual.h"
+/*
+ * Formerly, this code attempted to cache the function and type info
+ * looked up by fetch_fp_info, but only for the duration of a single
+ * transaction command (since in theory the info could change between
+ * commands). This was utterly useless, because postgres.c executes
+ * each fastpath call as a separate transaction command, and so the
+ * cached data could never actually have been reused. If it had worked
+ * as intended, it would have had problems anyway with dangling references
+ * in the FmgrInfo struct. So, forget about caching and just repeat the
+ * syscache fetches on each usage. They're not *that* expensive.
+ */
+struct fp_info
+{
+ Oid funcid;
+ FmgrInfo flinfo; /* function lookup info for funcid */
+ int16 arglen[FUNC_MAX_ARGS];
+ bool argbyval[FUNC_MAX_ARGS];
+ int16 retlen;
+ bool retbyval;
+};
+
+
+static void parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo);
+static void parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo);
+
+
/* ----------------
* GetOldFunctionMessage
*
pq_beginmessage(&buf, 'V');
- if (retlen != 0)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
- pq_sendbyte(&buf, 'G');
- if (retbyval)
- { /* by-value */
- pq_sendint(&buf, retlen, 4);
- pq_sendint(&buf, DatumGetInt32(retval), retlen);
+ /* New-style message */
+ /* XXX replace this with standard binary (or text!) output */
+ if (retlen != 0)
+ {
+ if (retbyval)
+ { /* by-value */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendint(&buf, DatumGetInt32(retval), retlen);
+ }
+ else
+ { /* by-reference ... */
+ if (retlen == -1)
+ { /* ... varlena */
+ struct varlena *v = PG_DETOAST_DATUM(retval);
+
+ pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+ pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ }
+ else
+ { /* ... fixed */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ }
+ }
}
else
- { /* by-reference ... */
- if (retlen == -1)
- { /* ... varlena */
- struct varlena *v = PG_DETOAST_DATUM(retval);
-
- pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
- pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ {
+ /* NULL marker */
+ pq_sendint(&buf, -1, 4);
+ }
+ }
+ else
+ {
+ /* Old-style message */
+ if (retlen != 0)
+ {
+ pq_sendbyte(&buf, 'G');
+ if (retbyval)
+ { /* by-value */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendint(&buf, DatumGetInt32(retval), retlen);
}
else
- { /* ... fixed */
- pq_sendint(&buf, retlen, 4);
- pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ { /* by-reference ... */
+ if (retlen == -1)
+ { /* ... varlena */
+ struct varlena *v = PG_DETOAST_DATUM(retval);
+
+ pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+ pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ }
+ else
+ { /* ... fixed */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ }
}
}
+ pq_sendbyte(&buf, '0');
}
- pq_sendbyte(&buf, '0');
pq_endmessage(&buf);
}
-/*
- * Formerly, this code attempted to cache the function and type info
- * looked up by fetch_fp_info, but only for the duration of a single
- * transaction command (since in theory the info could change between
- * commands). This was utterly useless, because postgres.c executes
- * each fastpath call as a separate transaction command, and so the
- * cached data could never actually have been reused. If it had worked
- * as intended, it would have had problems anyway with dangling references
- * in the FmgrInfo struct. So, forget about caching and just repeat the
- * syscache fetches on each usage. They're not *that* expensive.
- */
-struct fp_info
-{
- Oid funcid;
- FmgrInfo flinfo; /* function lookup info for funcid */
- int16 arglen[FUNC_MAX_ARGS];
- bool argbyval[FUNC_MAX_ARGS];
- int16 retlen;
- bool retbyval;
-};
-
/*
* fetch_fp_info
*
HandleFunctionRequest(StringInfo msgBuf)
{
Oid fid;
- int nargs;
AclResult aclresult;
FunctionCallInfoData fcinfo;
Datum retval;
- int i;
struct fp_info my_fp;
struct fp_info *fip;
/*
* Parse the buffer contents.
*/
- (void) pq_getmsgstring(msgBuf); /* dummy string */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ (void) pq_getmsgstring(msgBuf); /* dummy string */
+
fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
- nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
/*
* There used to be a lame attempt at caching lookup info here. Now we
SetQuerySnapshot();
/*
- * Prepare function call info block.
+ * Prepare function call info block and insert arguments.
*/
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
+ fcinfo.flinfo = &fip->flinfo;
+
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ parse_fcall_arguments(msgBuf, fip, &fcinfo);
+ else
+ parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+
+ /* Verify we reached the end of the message where expected. */
+ pq_getmsgend(msgBuf);
+
+ /* Okay, do it ... */
+ retval = FunctionCallInvoke(&fcinfo);
+
+ if (fcinfo.isnull)
+ SendFunctionResult(retval, fip->retbyval, 0);
+ else
+ SendFunctionResult(retval, fip->retbyval, fip->retlen);
+
+ return 0;
+}
+
+/*
+ * Parse function arguments in a 3.0 protocol message
+ */
+static void
+parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo)
+{
+ int nargs;
+ int i;
+ int numAFormats;
+ int16 *aformats = NULL;
+
+ /* Get the argument format codes */
+ numAFormats = pq_getmsgint(msgBuf, 2);
+ if (numAFormats > 0)
+ {
+ aformats = (int16 *) palloc(numAFormats * sizeof(int16));
+ for (i = 0; i < numAFormats; i++)
+ aformats[i] = pq_getmsgint(msgBuf, 2);
+ }
+
+ nargs = pq_getmsgint(msgBuf, 2); /* # of arguments */
+
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->flinfo.fn_nargs);
- MemSet(&fcinfo, 0, sizeof(fcinfo));
- fcinfo.flinfo = &fip->flinfo;
- fcinfo.nargs = nargs;
+ fcinfo->nargs = nargs;
/*
- * Copy supplied arguments into arg vector. Note there is no way for
- * frontend to specify a NULL argument --- this protocol is misdesigned.
+ * Copy supplied arguments into arg vector.
*/
for (i = 0; i < nargs; ++i)
{
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
argsize);
/* XXX should we demand argsize == fip->arglen[i] ? */
- fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+ fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
}
else
{ /* by-reference ... */
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
pq_copymsgbytes(msgBuf, p, argsize);
}
- fcinfo.arg[i] = PointerGetDatum(p);
+ fcinfo->arg[i] = PointerGetDatum(p);
}
}
- /* Verify we reached the end of the message where expected. */
- pq_getmsgend(msgBuf);
+ /* XXX for the moment, ignore result format code */
+ (void) pq_getmsgint(msgBuf, 2);
+}
-#ifdef NO_FASTPATH
- /* force a NULL return */
- retval = (Datum) 0;
- fcinfo.isnull = true;
-#else
- retval = FunctionCallInvoke(&fcinfo);
-#endif /* NO_FASTPATH */
+/*
+ * Parse function arguments in a 2.0 protocol message
+ */
+static void
+parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo)
+{
+ int nargs;
+ int i;
- if (fcinfo.isnull)
- SendFunctionResult(retval, fip->retbyval, 0);
- else
- SendFunctionResult(retval, fip->retbyval, fip->retlen);
+ nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
- return 0;
+ if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
+ elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
+ nargs, fip->flinfo.fn_nargs);
+
+ fcinfo->nargs = nargs;
+
+ /*
+ * Copy supplied arguments into arg vector. Note there is no way for
+ * frontend to specify a NULL argument --- this protocol is misdesigned.
+ */
+ for (i = 0; i < nargs; ++i)
+ {
+ int argsize;
+ char *p;
+
+ argsize = pq_getmsgint(msgBuf, 4);
+ if (fip->argbyval[i])
+ { /* by-value */
+ if (argsize < 1 || argsize > 4)
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+ argsize);
+ /* XXX should we demand argsize == fip->arglen[i] ? */
+ fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+ }
+ else
+ { /* by-reference ... */
+ if (fip->arglen[i] == -1)
+ { /* ... varlena */
+ if (argsize < 0)
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+ argsize);
+ p = palloc(argsize + VARHDRSZ);
+ VARATT_SIZEP(p) = argsize + VARHDRSZ;
+ pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
+ }
+ else
+ { /* ... fixed */
+ if (argsize != fip->arglen[i])
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
+ argsize, fip->arglen[i]);
+ p = palloc(argsize + 1); /* +1 in case argsize is 0 */
+ pq_copymsgbytes(msgBuf, p, argsize);
+ }
+ fcinfo->arg[i] = PointerGetDatum(p);
+ }
+ }
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.339 2003/05/08 14:49:04 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.340 2003/05/08 18:16:36 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
exec_simple_query(const char *query_string)
{
CommandDest dest = whereToSendOutput;
- DestReceiver *receiver;
MemoryContext oldcontext;
List *parsetree_list,
*parsetree_item;
if (save_log_statement_stats)
ResetUsage();
- /*
- * Create destination receiver object --- we can reuse it for all
- * queries in the string. Note it is created in MessageContext.
- */
- receiver = CreateDestReceiver(dest);
-
/*
* Start up a transaction command. All queries generated by the
* query_string will be in this same command block, *unless* we find a
List *querytree_list,
*plantree_list;
Portal portal;
+ DestReceiver *receiver;
+ int16 format;
/*
* Get the command name for use in status display (it also becomes the
/* If we got a cancel signal in analysis or planning, quit */
CHECK_FOR_INTERRUPTS();
- /*
- * Switch back to transaction context for execution.
- */
- MemoryContextSwitchTo(oldcontext);
-
/*
* Create unnamed portal to run the query or queries in.
* If there already is one, silently drop it.
MessageContext);
/*
- * Run the portal to completion, and then drop it.
+ * Start the portal. No parameters here.
*/
PortalStart(portal, NULL);
+ /*
+ * Select the appropriate output format: text unless we are doing
+ * a FETCH from a binary cursor. (Pretty grotty to have to do this
+ * here --- but it avoids grottiness in other places. Ah, the joys
+ * of backward compatibility...)
+ */
+ format = 0; /* TEXT is default */
+ if (IsA(parsetree, FetchStmt))
+ {
+ FetchStmt *stmt = (FetchStmt *) parsetree;
+
+ if (!stmt->ismove)
+ {
+ Portal fportal = GetPortalByName(stmt->portalname);
+
+ if (PortalIsValid(fportal) &&
+ (fportal->cursorOptions & CURSOR_OPT_BINARY))
+ format = 1; /* BINARY */
+ }
+ }
+ PortalSetResultFormat(portal, 1, &format);
+
+ /*
+ * Now we can create the destination receiver object.
+ */
+ receiver = CreateDestReceiver(dest, portal);
+
+ /*
+ * Switch back to transaction context for execution.
+ */
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Run the portal to completion, and then drop it (and the receiver).
+ */
(void) PortalRun(portal,
FETCH_ALL,
receiver,
receiver,
completionTag);
+ (*receiver->destroy) (receiver);
+
PortalDrop(portal, false);
if (!parsetree_list)
NullCommand(dest);
- (*receiver->destroy) (receiver);
-
QueryContext = NULL;
/*
{
const char *portal_name;
const char *stmt_name;
- int is_binary;
+ int numPFormats;
+ int16 *pformats = NULL;
int numParams;
+ int numRFormats;
+ int16 *rformats = NULL;
+ int i;
PreparedStatement *pstmt;
Portal portal;
ParamListInfo params;
*/
start_xact_command();
+ /* Switch back to message context */
+ MemoryContextSwitchTo(MessageContext);
+
/* Get the fixed part of the message */
portal_name = pq_getmsgstring(input_message);
stmt_name = pq_getmsgstring(input_message);
- is_binary = pq_getmsgbyte(input_message);
- numParams = pq_getmsgint(input_message, 4);
- if (is_binary)
- elog(ERROR, "Binary BIND not implemented yet");
+ /* Get the parameter format codes */
+ numPFormats = pq_getmsgint(input_message, 2);
+ if (numPFormats > 0)
+ {
+ pformats = (int16 *) palloc(numPFormats * sizeof(int16));
+ for (i = 0; i < numPFormats; i++)
+ pformats[i] = pq_getmsgint(input_message, 2);
+ }
+
+ /* Get the parameter value count */
+ numParams = pq_getmsgint(input_message, 2);
+
+ if (numPFormats > 1 && numPFormats != numParams)
+ elog(ERROR, "BIND message has %d parameter formats but %d parameters",
+ numPFormats, numParams);
/* Find prepared statement */
if (stmt_name[0] != '\0')
* Fetch parameters, if any, and store in the portal's memory context.
*
* In an aborted transaction, we can't risk calling user-defined functions,
- * so bind all parameters to null values.
+ * but we can't fail to Bind either, so bind all parameters to null values.
*/
if (numParams > 0)
{
bool isaborted = IsAbortedTransactionBlockState();
- int i = 0;
+ StringInfoData pbuf;
List *l;
MemoryContext oldContext;
+ /* Note that the string buffer lives in MessageContext */
+ initStringInfo(&pbuf);
+
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
params = (ParamListInfo)
palloc0((numParams + 1) * sizeof(ParamListInfoData));
+ i = 0;
foreach(l, pstmt->argtype_list)
{
Oid ptype = lfirsto(l);
+ int32 plength;
bool isNull;
- isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
+ plength = pq_getmsgint(input_message, 4);
+ isNull = (plength == -1);
+
if (!isNull)
{
- const char *ptext = pq_getmsgstring(input_message);
+ /* Reset pbuf to empty, and insert raw data into it */
+ pbuf.len = 0;
+ pbuf.data[0] = '\0';
+ pbuf.cursor = 0;
+
+ appendBinaryStringInfo(&pbuf,
+ pq_getmsgbytes(input_message, plength),
+ plength);
if (isaborted)
+ {
+ /* We don't bother to check the format in this case */
isNull = true;
+ }
else
{
- Oid typInput;
- Oid typElem;
-
- getTypeInputInfo(ptype, &typInput, &typElem);
- params[i].value =
- OidFunctionCall3(typInput,
- CStringGetDatum(ptext),
- ObjectIdGetDatum(typElem),
- Int32GetDatum(-1));
+ int16 pformat;
+
+ if (numPFormats > 1)
+ pformat = pformats[i];
+ else if (numPFormats > 0)
+ pformat = pformats[0];
+ else
+ pformat = 0; /* default = text */
+
+ if (pformat == 0)
+ {
+ Oid typInput;
+ Oid typElem;
+ char *pstring;
+
+ getTypeInputInfo(ptype, &typInput, &typElem);
+ /*
+ * Since stringinfo.c keeps a trailing null in
+ * place even for binary data, the contents of
+ * pbuf are a valid C string. We have to do
+ * encoding conversion before calling the typinput
+ * routine, though.
+ */
+ pstring = (char *)
+ pg_client_to_server((unsigned char *) pbuf.data,
+ plength);
+ params[i].value =
+ OidFunctionCall3(typInput,
+ CStringGetDatum(pstring),
+ ObjectIdGetDatum(typElem),
+ Int32GetDatum(-1));
+ /* Free result of encoding conversion, if any */
+ if (pstring != pbuf.data)
+ pfree(pstring);
+ }
+ else if (pformat == 1)
+ {
+ /* XXX something similar to above */
+ elog(ERROR, "Binary BIND not implemented yet");
+ }
+ else
+ {
+ elog(ERROR, "Invalid format code %d", pformat);
+ }
}
}
+
params[i].kind = PARAM_NUM;
params[i].id = i + 1;
params[i].isnull = isNull;
else
params = NULL;
+ /* Get the result format codes */
+ numRFormats = pq_getmsgint(input_message, 2);
+ if (numRFormats > 0)
+ {
+ rformats = (int16 *) palloc(numRFormats * sizeof(int16));
+ for (i = 0; i < numRFormats; i++)
+ rformats[i] = pq_getmsgint(input_message, 2);
+ }
+
pq_getmsgend(input_message);
/*
*/
PortalStart(portal, params);
+ /*
+ * Apply the result format requests to the portal.
+ */
+ PortalSetResultFormat(portal, numRFormats, rformats);
+
/*
* Send BindComplete.
*/
* Process an "Execute" message for a portal
*/
static void
-exec_execute_message(const char *portal_name, int is_binary, long max_rows)
+exec_execute_message(const char *portal_name, long max_rows)
{
CommandDest dest;
DestReceiver *receiver;
/* Adjust destination to tell printtup.c what to do */
dest = whereToSendOutput;
if (dest == Remote)
- dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
+ dest = RemoteExecute;
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
}
}
+ /*
+ * Create dest receiver in MessageContext (we don't want it in transaction
+ * context, because that may get deleted if portal contains VACUUM).
+ */
+ receiver = CreateDestReceiver(dest, portal);
+
/*
* Ensure we are in a transaction command (this should normally be
* the case already due to prior BIND).
/*
* Okay to run the portal.
*/
- receiver = CreateDestReceiver(dest);
-
if (max_rows <= 0)
max_rows = FETCH_ALL;
* First describe the parameters...
*/
pq_beginmessage(&buf, 't'); /* parameter description message type */
- pq_sendint(&buf, length(pstmt->argtype_list), 4);
+ pq_sendint(&buf, length(pstmt->argtype_list), 2);
foreach(l, pstmt->argtype_list)
{
targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
else
targetlist = NIL;
- SendRowDescriptionMessage(tupdesc, targetlist);
+ SendRowDescriptionMessage(tupdesc, targetlist, NULL);
}
else
pq_putemptymessage('n'); /* NoData */
List *targetlist;
if (portal->strategy == PORTAL_ONE_SELECT)
- targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+ targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
else
targetlist = NIL;
- SendRowDescriptionMessage(portal->tupDesc, targetlist);
+ SendRowDescriptionMessage(portal->tupDesc, targetlist,
+ portal->formats);
}
else
pq_putemptymessage('n'); /* NoData */
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.339 $ $Date: 2003/05/08 14:49:04 $\n");
+ puts("$Revision: 1.340 $ $Date: 2003/05/08 18:16:36 $\n");
}
/*
stmt_name = pq_getmsgstring(input_message);
query_string = pq_getmsgstring(input_message);
- numParams = pq_getmsgint(input_message, 4);
+ numParams = pq_getmsgint(input_message, 2);
if (numParams > 0)
{
int i;
case 'E': /* execute */
{
const char *portal_name;
- int is_binary;
int max_rows;
portal_name = pq_getmsgstring(input_message);
- is_binary = pq_getmsgbyte(input_message);
max_rows = pq_getmsgint(input_message, 4);
pq_getmsgend(input_message);
- exec_execute_message(portal_name, is_binary, max_rows);
+ exec_execute_message(portal_name, max_rows);
}
break;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.63 2003/05/06 21:01:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.64 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
#include "tcop/pquery.h"
CreateQueryDesc(Query *parsetree,
Plan *plantree,
DestReceiver *dest,
- const char *portalName,
ParamListInfo params,
bool doInstrument)
{
qd->parsetree = parsetree; /* parse tree */
qd->plantree = plantree; /* plan */
qd->dest = dest; /* output dest */
- qd->portalName = portalName; /* name, if dest is a portal */
qd->params = params; /* parameter values passed into query */
qd->doInstrument = doInstrument; /* instrumentation wanted? */
* parsetree: the query tree
* plan: the plan tree for the query
* params: any parameters needed
- * portalName: name of portal being used
* dest: where to send results
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
* in which to store a command completion status string.
ProcessQuery(Query *parsetree,
Plan *plan,
ParamListInfo params,
- const char *portalName,
DestReceiver *dest,
char *completionTag)
{
/*
* Create the QueryDesc object
*/
- queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params,
- false);
+ queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
/*
* Call ExecStart to prepare the plan for execution
queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
(Plan *) lfirst(portal->planTrees),
None_Receiver,
- portal->name,
params,
false);
/*
*/
portal->queryDesc = queryDesc;
/*
- * Remember tuple descriptor
+ * Remember tuple descriptor (computed by ExecutorStart)
*/
portal->tupDesc = queryDesc->tupDesc;
/*
portal->portalReady = true;
}
+/*
+ * PortalSetResultFormat
+ * Select the format codes for a portal's output.
+ *
+ * This must be run after PortalStart for a portal that will be read by
+ * a Remote or RemoteExecute destination. It is not presently needed for
+ * other destination types.
+ *
+ * formats[] is the client format request, as per Bind message conventions.
+ */
+void
+PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
+{
+ int natts;
+ int i;
+
+ /* Do nothing if portal won't return tuples */
+ if (portal->tupDesc == NULL)
+ return;
+ natts = portal->tupDesc->natts;
+ /* +1 avoids palloc(0) if no columns */
+ portal->formats = (int16 *)
+ MemoryContextAlloc(PortalGetHeapMemory(portal),
+ (natts + 1) * sizeof(int16));
+ if (nFormats > 1)
+ {
+ /* format specified for each column */
+ if (nFormats != natts)
+ elog(ERROR, "BIND message has %d result formats but query has %d columns",
+ nFormats, natts);
+ memcpy(portal->formats, formats, natts * sizeof(int16));
+ } else if (nFormats > 0)
+ {
+ /* single format specified, use for all columns */
+ int16 format1 = formats[0];
+
+ for (i = 0; i < natts; i++)
+ portal->formats[i] = format1;
+ }
+ else
+ {
+ /* use default format for all columns */
+ for (i = 0; i < natts; i++)
+ portal->formats[i] = 0;
+ }
+}
+
/*
* PortalRun
* Run a portal's query or queries.
DestReceiver *treceiver;
PortalCreateHoldStore(portal);
- treceiver = CreateTuplestoreDestReceiver(portal->holdStore,
- portal->holdContext);
+ treceiver = CreateDestReceiver(Tuplestore, portal);
PortalRunUtility(portal, lfirst(portal->parseTrees),
treceiver, NULL);
(*treceiver->destroy) (treceiver);
RunFromStore(Portal portal, ScanDirection direction, long count,
DestReceiver *dest)
{
- List *targetlist;
long current_tuple_count = 0;
- if (portal->strategy == PORTAL_ONE_SELECT)
- targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
- else
- targetlist = NIL;
-
- (*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc,
- targetlist);
+ (*dest->startup) (dest, CMD_SELECT, portal->tupDesc);
if (direction == NoMovementScanDirection)
{
* but the results will be discarded unless you use "simple Query"
* protocol.
*/
- if (dest->mydest == RemoteExecute ||
- dest->mydest == RemoteExecuteInternal)
+ if (dest->mydest == RemoteExecute)
dest = None_Receiver;
- if (altdest->mydest == RemoteExecute ||
- altdest->mydest == RemoteExecuteInternal)
+ if (altdest->mydest == RemoteExecute)
altdest = None_Receiver;
/*
{
/* statement can set tag string */
ProcessQuery(query, plan,
- portal->portalParams, portal->name,
+ portal->portalParams,
dest, completionTag);
}
else
{
/* stmt added by rewrite cannot set tag */
ProcessQuery(query, plan,
- portal->portalParams, portal->name,
+ portal->portalParams,
altdest, NULL);
}
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: printtup.h,v 1.26 2003/05/06 20:26:27 tgl Exp $
+ * $Id: printtup.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "tcop/dest.h"
-extern DestReceiver *printtup_create_DR(CommandDest dest);
+extern DestReceiver *printtup_create_DR(CommandDest dest, Portal portal);
-extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist);
+extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
+ int16 *formats);
extern void debugStartup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist);
+ TupleDesc typeinfo);
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
/* XXX these are really in executor/spi.c */
extern void spi_dest_startup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist);
+ TupleDesc typeinfo);
extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execdesc.h,v 1.23 2003/05/06 20:26:28 tgl Exp $
+ * $Id: execdesc.h,v 1.24 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Query *parsetree; /* rewritten parsetree */
Plan *plantree; /* planner's output */
DestReceiver *dest; /* the destination for tuple output */
- const char *portalName; /* name of portal, or NULL */
ParamListInfo params; /* param values being passed in */
bool doInstrument; /* TRUE requests runtime instrumentation */
/* in pquery.c */
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
- DestReceiver *dest, const char *portalName,
+ DestReceiver *dest,
ParamListInfo params,
bool doInstrument);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pqcomm.h,v 1.83 2003/05/06 21:51:42 tgl Exp $
+ * $Id: pqcomm.h,v 1.84 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,107) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,108) /* XXX temporary value */
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pqformat.h,v 1.15 2003/04/22 00:08:07 tgl Exp $
+ * $Id: pqformat.h,v 1.16 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void pq_beginmessage(StringInfo buf, char msgtype);
extern void pq_sendbyte(StringInfo buf, int byt);
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
-extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen);
+extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+ bool countincludesself);
extern void pq_sendstring(StringInfo buf, const char *str);
extern void pq_sendint(StringInfo buf, int i, int b);
extern void pq_endmessage(StringInfo buf);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: dest.h,v 1.37 2003/05/06 20:26:28 tgl Exp $
+ * $Id: dest.h,v 1.38 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
None, /* results are discarded */
Debug, /* results go to debugging output */
Remote, /* results sent to frontend process */
- RemoteInternal, /* results sent to frontend process in
- * internal (binary) form */
- SPI, /* results sent to SPI manager */
- Tuplestore, /* results sent to Tuplestore */
RemoteExecute, /* sent to frontend, in Execute command */
- RemoteExecuteInternal /* same, but binary format */
+ SPI, /* results sent to SPI manager */
+ Tuplestore /* results sent to Tuplestore */
} CommandDest;
/* ----------------
struct _DestReceiver
{
/* Called for each tuple to be output: */
- void (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
+ void (*receiveTuple) (HeapTuple tuple,
+ TupleDesc typeinfo,
DestReceiver *self);
/* Per-executor-run initialization and shutdown: */
- void (*startup) (DestReceiver *self, int operation,
- const char *portalName,
- TupleDesc typeinfo,
- List *targetlist);
+ void (*startup) (DestReceiver *self,
+ int operation,
+ TupleDesc typeinfo);
void (*shutdown) (DestReceiver *self);
/* Destroy the receiver object itself (if dynamically allocated) */
void (*destroy) (DestReceiver *self);
extern DestReceiver *None_Receiver; /* permanent receiver for None */
+/* This is a forward reference to utils/portal.h */
+
+typedef struct PortalData *Portal;
+
/* The primary destination management functions */
extern void BeginCommand(const char *commandTag, CommandDest dest);
-extern DestReceiver *CreateDestReceiver(CommandDest dest);
+extern DestReceiver *CreateDestReceiver(CommandDest dest, Portal portal);
extern void EndCommand(const char *commandTag, CommandDest dest);
/* Additional functions that go with destination management, more or less. */
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pquery.h,v 1.26 2003/05/06 20:26:28 tgl Exp $
+ * $Id: pquery.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ProcessQuery(Query *parsetree,
Plan *plan,
ParamListInfo params,
- const char *portalName,
DestReceiver *dest,
char *completionTag);
extern void PortalStart(Portal portal, ParamListInfo params);
+extern void PortalSetResultFormat(Portal portal, int nFormats,
+ int16 *formats);
+
extern bool PortalRun(Portal portal, long count,
DestReceiver *dest, DestReceiver *altdest,
char *completionTag);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: portal.h,v 1.43 2003/05/06 20:26:28 tgl Exp $
+ * $Id: portal.h,v 1.44 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
PORTAL_MULTI_QUERY
} PortalStrategy;
-typedef struct PortalData *Portal;
+/*
+ * Note: typedef Portal is declared in tcop/dest.h as
+ * typedef struct PortalData *Portal;
+ */
typedef struct PortalData
{
/* If portal returns tuples, this is their tupdesc: */
TupleDesc tupDesc; /* descriptor for result tuples */
+ /* and these are the format codes to use for the columns: */
+ int16 *formats; /* a format code for each column */
/*
* Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.134 2003/04/26 20:22:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.135 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void handleSendFailure(PGconn *conn);
static void handleSyncLoss(PGconn *conn, char id, int msgLength);
static int getRowDescriptions(PGconn *conn);
-static int getAnotherTuple(PGconn *conn, int binary);
+static int getAnotherTuple(PGconn *conn);
static int getParameterStatus(PGconn *conn);
static int getNotify(PGconn *conn);
-/* ---------------
- * Escaping arbitrary strings to get valid SQL strings/identifiers.
- *
- * Replaces "\\" with "\\\\" and "'" with "''".
- * length is the length of the buffer pointed to by
- * from. The buffer at to must be at least 2*length + 1 characters
- * long. A terminating NUL character is written.
- * ---------------
- */
-
-size_t
-PQescapeString(char *to, const char *from, size_t length)
-{
- const char *source = from;
- char *target = to;
- unsigned int remaining = length;
-
- while (remaining > 0)
- {
- switch (*source)
- {
- case '\\':
- *target = '\\';
- target++;
- *target = '\\';
- /* target and remaining are updated below. */
- break;
-
- case '\'':
- *target = '\'';
- target++;
- *target = '\'';
- /* target and remaining are updated below. */
- break;
-
- default:
- *target = *source;
- /* target and remaining are updated below. */
- }
- source++;
- target++;
- remaining--;
- }
-
- /* Write the terminating NUL character. */
- *target = '\0';
-
- return target - to;
-}
-
-/*
- * PQescapeBytea - converts from binary string to the
- * minimal encoding necessary to include the string in an SQL
- * INSERT statement with a bytea type column as the target.
- *
- * The following transformations are applied
- * '\0' == ASCII 0 == \\000
- * '\'' == ASCII 39 == \'
- * '\\' == ASCII 92 == \\\\
- * anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
- */
-unsigned char *
-PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
-{
- const unsigned char *vp;
- unsigned char *rp;
- unsigned char *result;
- size_t i;
- size_t len;
-
- /*
- * empty string has 1 char ('\0')
- */
- len = 1;
-
- vp = bintext;
- for (i = binlen; i > 0; i--, vp++)
- {
- if (*vp == 0 || *vp >= 0x80)
- len += 5; /* '5' is for '\\ooo' */
- else if (*vp == '\'')
- len += 2;
- else if (*vp == '\\')
- len += 4;
- else
- len++;
- }
-
- rp = result = (unsigned char *) malloc(len);
- if (rp == NULL)
- return NULL;
-
- vp = bintext;
- *bytealen = len;
-
- for (i = binlen; i > 0; i--, vp++)
- {
- if (*vp == 0 || *vp >= 0x80)
- {
- (void) sprintf(rp, "\\\\%03o", *vp);
- rp += 5;
- }
- else if (*vp == '\'')
- {
- rp[0] = '\\';
- rp[1] = '\'';
- rp += 2;
- }
- else if (*vp == '\\')
- {
- rp[0] = '\\';
- rp[1] = '\\';
- rp[2] = '\\';
- rp[3] = '\\';
- rp += 4;
- }
- else
- *rp++ = *vp;
- }
- *rp = '\0';
-
- return result;
-}
-
-/*
- * PQunescapeBytea - converts the null terminated string representation
- * of a bytea, strtext, into binary, filling a buffer. It returns a
- * pointer to the buffer which is NULL on error, and the size of the
- * buffer in retbuflen. The pointer may subsequently be used as an
- * argument to the function free(3). It is the reverse of PQescapeBytea.
- *
- * The following transformations are reversed:
- * '\0' == ASCII 0 == \000
- * '\'' == ASCII 39 == \'
- * '\\' == ASCII 92 == \\
- *
- * States:
- * 0 normal 0->1->2->3->4
- * 1 \ 1->5
- * 2 \0 1->6
- * 3 \00
- * 4 \000
- * 5 \'
- * 6 \\
- */
-unsigned char *
-PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
-{
- size_t buflen;
- unsigned char *buffer,
- *bp;
- const unsigned char *sp;
- unsigned int state = 0;
-
- if (strtext == NULL)
- return NULL;
- buflen = strlen(strtext); /* will shrink, also we discover if
- * strtext */
- buffer = (unsigned char *) malloc(buflen); /* isn't NULL terminated */
- if (buffer == NULL)
- return NULL;
- for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
- {
- switch (state)
- {
- case 0:
- if (*sp == '\\')
- state = 1;
- *bp = *sp;
- break;
- case 1:
- if (*sp == '\'') /* state=5 */
- { /* replace \' with 39 */
- bp--;
- *bp = '\'';
- buflen--;
- state = 0;
- }
- else if (*sp == '\\') /* state=6 */
- { /* replace \\ with 92 */
- bp--;
- *bp = '\\';
- buflen--;
- state = 0;
- }
- else
- {
- if (isdigit(*sp))
- state = 2;
- else
- state = 0;
- *bp = *sp;
- }
- break;
- case 2:
- if (isdigit(*sp))
- state = 3;
- else
- state = 0;
- *bp = *sp;
- break;
- case 3:
- if (isdigit(*sp)) /* state=4 */
- {
- int v;
-
- bp -= 3;
- sscanf(sp - 2, "%03o", &v);
- *bp = v;
- buflen -= 3;
- state = 0;
- }
- else
- {
- *bp = *sp;
- state = 0;
- }
- break;
- }
- }
- buffer = realloc(buffer, buflen);
- if (buffer == NULL)
- return NULL;
-
- *retbuflen = buflen;
- return buffer;
-}
-
-
-/*
- * PQfreemem - safely frees memory allocated
- *
- * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
- * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
- */
-void PQfreemem(void *ptr)
-{
- free(ptr);
-}
-
/* ----------------
* Space management for PGresult.
return;
}
if (msgLength > 30000 &&
- !(id == 'T' || id == 'D' || id == 'B' || id == 'd'))
+ !(id == 'T' || id == 'D' || id == 'd'))
{
handleSyncLoss(conn, id, msgLength);
return;
return;
}
break;
- case 'D': /* ASCII data tuple */
+ case 'D': /* Data Row */
if (conn->result != NULL)
{
/* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, FALSE))
+ if (getAnotherTuple(conn))
return;
}
else
conn->inCursor += msgLength;
}
break;
- case 'B': /* Binary data tuple */
- if (conn->result != NULL)
- {
- /* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, TRUE))
- return;
- }
- else
- {
- snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
- DONOTICE(conn, noticeWorkspace);
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- }
- break;
case 'G': /* Start Copy In */
if (pqGetc(&conn->copy_is_binary, conn))
return;
+ /* XXX we currently ignore the rest of the message */
+ conn->inCursor = conn->inStart + 5 + msgLength;
conn->asyncStatus = PGASYNC_COPY_IN;
break;
case 'H': /* Start Copy Out */
if (pqGetc(&conn->copy_is_binary, conn))
return;
+ /* XXX we currently ignore the rest of the message */
+ conn->inCursor = conn->inStart + 5 + msgLength;
conn->asyncStatus = PGASYNC_COPY_OUT;
conn->copy_already_done = 0;
break;
int typid;
int typlen;
int atttypmod;
+ int format;
if (pqGets(&conn->workBuffer, conn) ||
pqGetInt(&tableid, 4, conn) ||
pqGetInt(&columnid, 2, conn) ||
pqGetInt(&typid, 4, conn) ||
pqGetInt(&typlen, 2, conn) ||
- pqGetInt(&atttypmod, 4, conn))
+ pqGetInt(&atttypmod, 4, conn) ||
+ pqGetInt(&format, 2, conn))
{
PQclear(result);
return EOF;
*/
columnid = (int) ((int16) columnid);
typlen = (int) ((int16) typlen);
+ format = (int) ((int16) format);
result->attDescs[i].name = pqResultStrdup(result,
conn->workBuffer.data);
result->attDescs[i].typid = typid;
result->attDescs[i].typlen = typlen;
result->attDescs[i].atttypmod = atttypmod;
- /* XXX todo: save tableid/columnid too */
+ /* XXX todo: save tableid/columnid, format too */
}
/* Success! */
}
/*
- * parseInput subroutine to read a 'B' or 'D' (row data) message.
+ * 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.
*
*/
static int
-getAnotherTuple(PGconn *conn, int binary)
+getAnotherTuple(PGconn *conn)
{
PGresult *result = conn->result;
int nfields = result->numAttributes;
PGresAttValue *tup;
-
- /* the backend sends us a bitmap of which attributes are null */
- char std_bitmap[64]; /* used unless it doesn't fit */
- char *bitmap = std_bitmap;
- int i;
- size_t nbytes; /* the number of bytes in bitmap */
- char bmap; /* One byte of the bitmap */
- int bitmap_index; /* Its index */
- int bitcnt; /* number of bits examined in current byte */
+ int tupnfields; /* # fields from tuple */
int vlen; /* length of the current field value */
-
- result->binary = binary;
+ int i;
/* Allocate tuple space if first time for this data message */
if (conn->curTuple == NULL)
}
tup = conn->curTuple;
- /* Get the null-value bitmap */
- nbytes = (nfields + BYTELEN - 1) / BYTELEN;
- /* malloc() only for unusually large field counts... */
- if (nbytes > sizeof(std_bitmap))
- bitmap = (char *) malloc(nbytes);
+ /* Get the field count and make sure it's what we expect */
+ if (pqGetInt(&tupnfields, 2, conn))
+ return EOF;
- if (pqGetnchar(bitmap, nbytes, conn))
- goto EOFexit;
+ if (tupnfields != nfields)
+ {
+ /* Replace partially constructed result with an error result */
+ pqClearAsyncResult(conn);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unexpected field count in D message\n"));
+ saveErrorResult(conn);
+ conn->asyncStatus = PGASYNC_READY;
+ /* Discard the failed message by pretending we read it */
+ return 0;
+ }
/* Scan the fields */
- bitmap_index = 0;
- bmap = bitmap[bitmap_index];
- bitcnt = 0;
-
for (i = 0; i < nfields; i++)
{
- if (!(bmap & 0200))
+ /* get the value length */
+ if (pqGetInt(&vlen, 4, conn))
+ return EOF;
+ if (vlen == -1)
{
- /* if the field value is absent, make it a null string */
+ /* null field */
tup[i].value = result->null_field;
tup[i].len = NULL_LEN;
+ continue;
}
- else
+ if (vlen < 0)
+ vlen = 0;
+ if (tup[i].value == NULL)
{
- /* get the value length (the first four bytes are for length) */
- if (pqGetInt(&vlen, 4, conn))
- goto EOFexit;
- if (binary == 0)
- vlen = vlen - 4;
- if (vlen < 0)
- vlen = 0;
+ tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
if (tup[i].value == NULL)
- {
- tup[i].value = (char *) pqResultAlloc(result, vlen + 1, (bool) binary);
- if (tup[i].value == NULL)
- goto outOfMemory;
- }
- tup[i].len = vlen;
- /* read in the value */
- if (vlen > 0)
- if (pqGetnchar((char *) (tup[i].value), vlen, conn))
- goto EOFexit;
- /* we have to terminate this ourselves */
- tup[i].value[vlen] = '\0';
- }
- /* advance the bitmap stuff */
- bitcnt++;
- if (bitcnt == BYTELEN)
- {
- bitmap_index++;
- bmap = bitmap[bitmap_index];
- bitcnt = 0;
+ goto outOfMemory;
}
- else
- bmap <<= 1;
+ tup[i].len = vlen;
+ /* read in the value */
+ if (vlen > 0)
+ if (pqGetnchar((char *) (tup[i].value), vlen, conn))
+ return EOF;
+ /* we have to terminate this ourselves */
+ tup[i].value[vlen] = '\0';
}
/* Success! Store the completed tuple in the result */
/* and reset for a new message */
conn->curTuple = NULL;
- if (bitmap != std_bitmap)
- free(bitmap);
return 0;
outOfMemory:
libpq_gettext("out of memory\n"));
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
conn->asyncStatus = PGASYNC_READY;
- /* Discard the failed message --- good idea? */
- conn->inStart = conn->inEnd;
-
-EOFexit:
- if (bitmap != std_bitmap)
- free(bitmap);
- return EOF;
+ /* Discard the failed message by pretending we read it */
+ return 0;
}
return NULL;
}
- if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
- pqPuts("", conn) < 0 || /* useless string */
- pqPutInt(fnid, 4, conn) < 0 || /* function id */
- pqPutInt(nargs, 4, conn) < 0) /* # of args */
+ if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
+ pqPutInt(fnid, 4, conn) < 0 || /* function id */
+ pqPutInt(1, 2, conn) < 0 || /* # of format codes */
+ pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
+ pqPutInt(nargs, 2, conn) < 0) /* # of args */
{
handleSendFailure(conn);
return NULL;
handleSendFailure(conn);
return NULL;
}
+ if (args[i].len == -1)
+ continue; /* it's NULL */
if (args[i].isint)
{
- if (pqPutInt(args[i].u.integer, 4, conn))
+ if (pqPutInt(args[i].u.integer, args[i].len, conn))
{
handleSendFailure(conn);
return NULL;
}
}
+ if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */
+ {
+ handleSendFailure(conn);
+ return NULL;
+ }
+
if (pqPutMsgEnd(conn) < 0 ||
pqFlush(conn))
{
break;
}
if (msgLength > 30000 &&
- !(id == 'T' || id == 'D' || id == 'B' || id == 'd' || id == 'V'))
+ !(id == 'T' || id == 'D' || id == 'd' || id == 'V'))
{
handleSyncLoss(conn, id, msgLength);
break;
switch (id)
{
case 'V': /* function result */
- if (pqGetc(&id, conn))
+ if (pqGetInt(actual_result_len, 4, conn))
continue;
- if (id == 'G')
+ if (*actual_result_len != -1)
{
- /* function returned nonempty value */
- if (pqGetInt(actual_result_len, 4, conn))
- continue;
if (result_is_int)
{
- if (pqGetInt(result_buf, 4, conn))
+ if (pqGetInt(result_buf, *actual_result_len, conn))
continue;
}
else
conn))
continue;
}
- if (pqGetc(&id, conn)) /* get the last '0' */
- continue;
- }
- if (id == '0')
- {
- /* correctly finished function result message */
- status = PGRES_COMMAND_OK;
- }
- else
- {
- /* The backend violates the protocol. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("protocol error: id=0x%x\n"),
- id);
- saveErrorResult(conn);
- conn->inStart += 5 + msgLength;
- return prepareAsyncResult(conn);
}
+ /* correctly finished function result message */
+ status = PGRES_COMMAND_OK;
break;
case 'E': /* error return */
if (pqGetErrorNotice(conn, true))
return (pqFlush(conn));
}
+
+/*
+ * PQfreemem - safely frees memory allocated
+ *
+ * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
+ * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
+ */
+void PQfreemem(void *ptr)
+{
+ free(ptr);
+}
+
/*
* PQfreeNotify - free's the memory associated with a PGnotify
*
{
PQfreemem(notify);
}
+
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from. The buffer at to must be at least 2*length + 1 characters
+ * long. A terminating NUL character is written.
+ * ---------------
+ */
+
+size_t
+PQescapeString(char *to, const char *from, size_t length)
+{
+ const char *source = from;
+ char *target = to;
+ unsigned int remaining = length;
+
+ while (remaining > 0)
+ {
+ switch (*source)
+ {
+ case '\\':
+ *target = '\\';
+ target++;
+ *target = '\\';
+ /* target and remaining are updated below. */
+ break;
+
+ case '\'':
+ *target = '\'';
+ target++;
+ *target = '\'';
+ /* target and remaining are updated below. */
+ break;
+
+ default:
+ *target = *source;
+ /* target and remaining are updated below. */
+ }
+ source++;
+ target++;
+ remaining--;
+ }
+
+ /* Write the terminating NUL character. */
+ *target = '\0';
+
+ return target - to;
+}
+
+/*
+ * PQescapeBytea - converts from binary string to the
+ * minimal encoding necessary to include the string in an SQL
+ * INSERT statement with a bytea type column as the target.
+ *
+ * The following transformations are applied
+ * '\0' == ASCII 0 == \\000
+ * '\'' == ASCII 39 == \'
+ * '\\' == ASCII 92 == \\\\
+ * anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
+ */
+unsigned char *
+PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
+{
+ const unsigned char *vp;
+ unsigned char *rp;
+ unsigned char *result;
+ size_t i;
+ size_t len;
+
+ /*
+ * empty string has 1 char ('\0')
+ */
+ len = 1;
+
+ vp = bintext;
+ for (i = binlen; i > 0; i--, vp++)
+ {
+ if (*vp == 0 || *vp >= 0x80)
+ len += 5; /* '5' is for '\\ooo' */
+ else if (*vp == '\'')
+ len += 2;
+ else if (*vp == '\\')
+ len += 4;
+ else
+ len++;
+ }
+
+ rp = result = (unsigned char *) malloc(len);
+ if (rp == NULL)
+ return NULL;
+
+ vp = bintext;
+ *bytealen = len;
+
+ for (i = binlen; i > 0; i--, vp++)
+ {
+ if (*vp == 0 || *vp >= 0x80)
+ {
+ (void) sprintf(rp, "\\\\%03o", *vp);
+ rp += 5;
+ }
+ else if (*vp == '\'')
+ {
+ rp[0] = '\\';
+ rp[1] = '\'';
+ rp += 2;
+ }
+ else if (*vp == '\\')
+ {
+ rp[0] = '\\';
+ rp[1] = '\\';
+ rp[2] = '\\';
+ rp[3] = '\\';
+ rp += 4;
+ }
+ else
+ *rp++ = *vp;
+ }
+ *rp = '\0';
+
+ return result;
+}
+
+/*
+ * PQunescapeBytea - converts the null terminated string representation
+ * of a bytea, strtext, into binary, filling a buffer. It returns a
+ * pointer to the buffer which is NULL on error, and the size of the
+ * buffer in retbuflen. The pointer may subsequently be used as an
+ * argument to the function free(3). It is the reverse of PQescapeBytea.
+ *
+ * The following transformations are reversed:
+ * '\0' == ASCII 0 == \000
+ * '\'' == ASCII 39 == \'
+ * '\\' == ASCII 92 == \\
+ *
+ * States:
+ * 0 normal 0->1->2->3->4
+ * 1 \ 1->5
+ * 2 \0 1->6
+ * 3 \00
+ * 4 \000
+ * 5 \'
+ * 6 \\
+ */
+unsigned char *
+PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
+{
+ size_t buflen;
+ unsigned char *buffer,
+ *bp;
+ const unsigned char *sp;
+ unsigned int state = 0;
+
+ if (strtext == NULL)
+ return NULL;
+ buflen = strlen(strtext); /* will shrink, also we discover if
+ * strtext */
+ buffer = (unsigned char *) malloc(buflen); /* isn't NULL terminated */
+ if (buffer == NULL)
+ return NULL;
+ for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
+ {
+ switch (state)
+ {
+ case 0:
+ if (*sp == '\\')
+ state = 1;
+ *bp = *sp;
+ break;
+ case 1:
+ if (*sp == '\'') /* state=5 */
+ { /* replace \' with 39 */
+ bp--;
+ *bp = '\'';
+ buflen--;
+ state = 0;
+ }
+ else if (*sp == '\\') /* state=6 */
+ { /* replace \\ with 92 */
+ bp--;
+ *bp = '\\';
+ buflen--;
+ state = 0;
+ }
+ else
+ {
+ if (isdigit(*sp))
+ state = 2;
+ else
+ state = 0;
+ *bp = *sp;
+ }
+ break;
+ case 2:
+ if (isdigit(*sp))
+ state = 3;
+ else
+ state = 0;
+ *bp = *sp;
+ break;
+ case 3:
+ if (isdigit(*sp)) /* state=4 */
+ {
+ int v;
+
+ bp -= 3;
+ sscanf(sp - 2, "%03o", &v);
+ *bp = v;
+ buflen -= 3;
+ state = 0;
+ }
+ else
+ {
+ *bp = *sp;
+ state = 0;
+ }
+ break;
+ }
+ }
+ buffer = realloc(buffer, buflen);
+ if (buffer == NULL)
+ return NULL;
+
+ *retbuflen = buflen;
+ return buffer;
+}
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.68 2003/05/06 21:51:42 tgl Exp $
+ * $Id: libpq-int.h,v 1.69 2003/05/08 18:16:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
-#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,107) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,108) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.