mirror of
https://github.com/postgres/postgres.git
synced 2025-06-07 11:02:12 +03:00
Update 3.0 protocol support to match recent agreements about how to
handle multiple 'formats' for data I/O. Restructure CommandDest and DestReceiver stuff one more time (it's finally starting to look a bit clean though). Code now matches latest 3.0 protocol document as far as message formats go --- but there is no support for binary I/O yet.
This commit is contained in:
parent
5e7a5c9511
commit
c0a8c3ac13
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -20,12 +20,17 @@
|
|||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
static void printtup_startup(DestReceiver *self, int operation,
|
static void printtup_startup(DestReceiver *self, int operation,
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist);
|
TupleDesc typeinfo);
|
||||||
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
|
static void printtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||||
static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
|
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_shutdown(DestReceiver *self);
|
||||||
static void printtup_destroy(DestReceiver *self);
|
static void printtup_destroy(DestReceiver *self);
|
||||||
|
|
||||||
@ -50,6 +55,7 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
DestReceiver pub; /* publicly-known function pointers */
|
DestReceiver pub; /* publicly-known function pointers */
|
||||||
|
Portal portal; /* the Portal we are printing from */
|
||||||
bool sendDescrip; /* send RowDescription at startup? */
|
bool sendDescrip; /* send RowDescription at startup? */
|
||||||
TupleDesc attrinfo; /* The attr info we are set up for */
|
TupleDesc attrinfo; /* The attr info we are set up for */
|
||||||
int nattrs;
|
int nattrs;
|
||||||
@ -61,43 +67,33 @@ typedef struct
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
DestReceiver *
|
DestReceiver *
|
||||||
printtup_create_DR(CommandDest dest)
|
printtup_create_DR(CommandDest dest, Portal portal)
|
||||||
{
|
{
|
||||||
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
|
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;
|
* In protocol 2.0 the Bind message does not exist, so there is
|
||||||
sendDescrip = true;
|
* no way for the columns to have different print formats; it's
|
||||||
break;
|
* sufficient to look at the first one.
|
||||||
case RemoteInternal:
|
*/
|
||||||
isBinary = true;
|
if (portal->formats && portal->formats[0] != 0)
|
||||||
sendDescrip = true;
|
self->pub.receiveTuple = printtup_internal_20;
|
||||||
break;
|
else
|
||||||
case RemoteExecute:
|
self->pub.receiveTuple = printtup_20;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
|
|
||||||
self->pub.startup = printtup_startup;
|
self->pub.startup = printtup_startup;
|
||||||
self->pub.shutdown = printtup_shutdown;
|
self->pub.shutdown = printtup_shutdown;
|
||||||
self->pub.destroy = printtup_destroy;
|
self->pub.destroy = printtup_destroy;
|
||||||
self->pub.mydest = dest;
|
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->attrinfo = NULL;
|
||||||
self->nattrs = 0;
|
self->nattrs = 0;
|
||||||
@ -107,10 +103,10 @@ printtup_create_DR(CommandDest dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
printtup_startup(DestReceiver *self, int operation,
|
printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
|
||||||
{
|
{
|
||||||
DR_printtup *myState = (DR_printtup *) self;
|
DR_printtup *myState = (DR_printtup *) self;
|
||||||
|
Portal portal = myState->portal;
|
||||||
|
|
||||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
|
||||||
{
|
{
|
||||||
@ -119,7 +115,9 @@ printtup_startup(DestReceiver *self, int operation,
|
|||||||
*
|
*
|
||||||
* If portal name not specified, use "blank" portal.
|
* If portal name not specified, use "blank" portal.
|
||||||
*/
|
*/
|
||||||
if (portalName == NULL)
|
const char *portalName = portal->name;
|
||||||
|
|
||||||
|
if (portalName == NULL || portalName[0] == '\0')
|
||||||
portalName = "blank";
|
portalName = "blank";
|
||||||
|
|
||||||
pq_puttextmessage('P', portalName);
|
pq_puttextmessage('P', portalName);
|
||||||
@ -130,7 +128,16 @@ printtup_startup(DestReceiver *self, int operation,
|
|||||||
* then we send back the tuple descriptor of the tuples.
|
* then we send back the tuple descriptor of the tuples.
|
||||||
*/
|
*/
|
||||||
if (operation == CMD_SELECT && myState->sendDescrip)
|
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
|
* We could set up the derived attr info at this time, but we postpone it
|
||||||
@ -150,11 +157,13 @@ printtup_startup(DestReceiver *self, int operation,
|
|||||||
* Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
|
* Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
|
||||||
* or some similar function; it does not contain a full set of fields.
|
* 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
|
* 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
|
* 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.
|
* 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
|
void
|
||||||
SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
|
SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
|
||||||
{
|
{
|
||||||
Form_pg_attribute *attrs = typeinfo->attrs;
|
Form_pg_attribute *attrs = typeinfo->attrs;
|
||||||
int natts = typeinfo->natts;
|
int natts = typeinfo->natts;
|
||||||
@ -198,6 +207,14 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
|
|||||||
if (proto >= 2)
|
if (proto >= 2)
|
||||||
pq_sendint(&buf, attrs[i]->atttypmod,
|
pq_sendint(&buf, attrs[i]->atttypmod,
|
||||||
sizeof(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);
|
pq_endmessage(&buf);
|
||||||
}
|
}
|
||||||
@ -228,11 +245,98 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* printtup
|
* printtup --- print a tuple in protocol 3.0
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
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;
|
DR_printtup *myState = (DR_printtup *) self;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
@ -300,7 +404,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
ObjectIdGetDatum(thisState->typelem),
|
ObjectIdGetDatum(thisState->typelem),
|
||||||
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
|
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
|
||||||
|
|
||||||
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
|
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
|
||||||
|
|
||||||
/* Clean up detoasted copy, if any */
|
/* Clean up detoasted copy, if any */
|
||||||
if (attr != origattr)
|
if (attr != origattr)
|
||||||
@ -310,7 +414,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
outputstr = "<unprintable>";
|
outputstr = "<unprintable>";
|
||||||
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
|
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,38 +467,23 @@ printatt(unsigned attributeId,
|
|||||||
attributeP->attbyval ? 't' : 'f');
|
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
|
* debugStartup - prepare to print tuples for an interactive backend
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
debugStartup(DestReceiver *self, int operation,
|
debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
|
||||||
{
|
{
|
||||||
|
int natts = typeinfo->natts;
|
||||||
|
Form_pg_attribute *attinfo = typeinfo->attrs;
|
||||||
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* show the return type of the tuples
|
* show the return type of the tuples
|
||||||
*/
|
*/
|
||||||
if (portalName == NULL)
|
for (i = 0; i < natts; ++i)
|
||||||
portalName = "blank";
|
printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
|
||||||
|
printf("\t----\n");
|
||||||
showatts(portalName, typeinfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -448,15 +537,16 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* printtup_internal
|
* printtup_internal_20 --- print a binary tuple in protocol 2.0
|
||||||
* We use a different data prefix, e.g. 'B' instead of 'D' to
|
*
|
||||||
|
* We use a different message type, i.e. 'B' instead of 'D' to
|
||||||
* indicate a tuple in internal (binary) form.
|
* 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
|
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;
|
DR_printtup *myState = (DR_printtup *) self;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -125,8 +125,8 @@ static int server_encoding;
|
|||||||
/*
|
/*
|
||||||
* Internal communications functions
|
* Internal communications functions
|
||||||
*/
|
*/
|
||||||
static void SendCopyBegin(bool binary);
|
static void SendCopyBegin(bool binary, int natts);
|
||||||
static void ReceiveCopyBegin(bool binary);
|
static void ReceiveCopyBegin(bool binary, int natts);
|
||||||
static void SendCopyEnd(bool binary);
|
static void SendCopyEnd(bool binary);
|
||||||
static void CopySendData(void *databuf, int datasize);
|
static void CopySendData(void *databuf, int datasize);
|
||||||
static void CopySendString(const char *str);
|
static void CopySendString(const char *str);
|
||||||
@ -143,15 +143,20 @@ static void CopyDonePeek(int c, bool pickup);
|
|||||||
* in past protocol redesigns.
|
* in past protocol redesigns.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SendCopyBegin(bool binary)
|
SendCopyBegin(bool binary, int natts)
|
||||||
{
|
{
|
||||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||||
{
|
{
|
||||||
/* new way */
|
/* new way */
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
int16 format = (binary ? 1 : 0);
|
||||||
|
int i;
|
||||||
|
|
||||||
pq_beginmessage(&buf, 'H');
|
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);
|
pq_endmessage(&buf);
|
||||||
copy_dest = COPY_NEW_FE;
|
copy_dest = COPY_NEW_FE;
|
||||||
copy_msgbuf = makeStringInfo();
|
copy_msgbuf = makeStringInfo();
|
||||||
@ -179,15 +184,20 @@ SendCopyBegin(bool binary)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ReceiveCopyBegin(bool binary)
|
ReceiveCopyBegin(bool binary, int natts)
|
||||||
{
|
{
|
||||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||||
{
|
{
|
||||||
/* new way */
|
/* new way */
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
int16 format = (binary ? 1 : 0);
|
||||||
|
int i;
|
||||||
|
|
||||||
pq_beginmessage(&buf, 'G');
|
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);
|
pq_endmessage(&buf);
|
||||||
copy_dest = COPY_NEW_FE;
|
copy_dest = COPY_NEW_FE;
|
||||||
copy_msgbuf = makeStringInfo();
|
copy_msgbuf = makeStringInfo();
|
||||||
@ -682,7 +692,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
if (pipe)
|
if (pipe)
|
||||||
{
|
{
|
||||||
if (IsUnderPostmaster)
|
if (IsUnderPostmaster)
|
||||||
ReceiveCopyBegin(binary);
|
ReceiveCopyBegin(binary, length(attnumlist));
|
||||||
else
|
else
|
||||||
copy_file = stdin;
|
copy_file = stdin;
|
||||||
}
|
}
|
||||||
@ -724,7 +734,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
if (pipe)
|
if (pipe)
|
||||||
{
|
{
|
||||||
if (IsUnderPostmaster)
|
if (IsUnderPostmaster)
|
||||||
SendCopyBegin(binary);
|
SendCopyBegin(binary, length(attnumlist));
|
||||||
else
|
else
|
||||||
copy_file = stdout;
|
copy_file = stdout;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -179,7 +179,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
|
|||||||
plan = planner(query, isCursor, cursorOptions);
|
plan = planner(query, isCursor, cursorOptions);
|
||||||
|
|
||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, NULL,
|
queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
|
||||||
stmt->analyze);
|
stmt->analyze);
|
||||||
|
|
||||||
ExplainOnePlan(queryDesc, stmt, tstate);
|
ExplainOnePlan(queryDesc, stmt, tstate);
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include "commands/portalcmds.h"
|
#include "commands/portalcmds.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/tstoreReceiver.h"
|
|
||||||
#include "optimizer/planner.h"
|
#include "optimizer/planner.h"
|
||||||
#include "rewrite/rewriteHandler.h"
|
#include "rewrite/rewriteHandler.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
@ -145,7 +144,6 @@ PerformPortalFetch(FetchStmt *stmt,
|
|||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
DestReceiver *mydest = dest;
|
|
||||||
Portal portal;
|
Portal portal;
|
||||||
long nprocessed;
|
long nprocessed;
|
||||||
|
|
||||||
@ -168,35 +166,21 @@ PerformPortalFetch(FetchStmt *stmt,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Adjust dest if needed. MOVE wants destination None */
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
if (stmt->ismove)
|
if (stmt->ismove)
|
||||||
mydest = CreateDestReceiver(None);
|
dest = None_Receiver;
|
||||||
else if (dest->mydest == Remote &&
|
|
||||||
(portal->cursorOptions & CURSOR_OPT_BINARY))
|
|
||||||
mydest = CreateDestReceiver(RemoteInternal);
|
|
||||||
|
|
||||||
/* Do it */
|
/* Do it */
|
||||||
nprocessed = PortalRunFetch(portal,
|
nprocessed = PortalRunFetch(portal,
|
||||||
stmt->direction,
|
stmt->direction,
|
||||||
stmt->howMany,
|
stmt->howMany,
|
||||||
mydest);
|
dest);
|
||||||
|
|
||||||
/* Return command status if wanted */
|
/* Return command status if wanted */
|
||||||
if (completionTag)
|
if (completionTag)
|
||||||
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
|
snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
|
||||||
stmt->ismove ? "MOVE" : "FETCH",
|
stmt->ismove ? "MOVE" : "FETCH",
|
||||||
nprocessed);
|
nprocessed);
|
||||||
|
|
||||||
/* Clean up if we created a local destination */
|
|
||||||
if (mydest != dest)
|
|
||||||
(mydest->destroy) (mydest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -329,8 +313,7 @@ PersistHoldablePortal(Portal portal)
|
|||||||
ExecutorRewind(queryDesc);
|
ExecutorRewind(queryDesc);
|
||||||
|
|
||||||
/* Change the destination to output to the tuplestore */
|
/* Change the destination to output to the tuplestore */
|
||||||
queryDesc->dest = CreateTuplestoreDestReceiver(portal->holdStore,
|
queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
|
||||||
portal->holdContext);
|
|
||||||
|
|
||||||
/* Fetch the result set into the tuplestore */
|
/* Fetch the result set into the tuplestore */
|
||||||
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
|
ExecutorRun(queryDesc, ForwardScanDirection, 0L);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -528,7 +528,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a QueryDesc requesting no output */
|
/* Create a QueryDesc requesting no output */
|
||||||
qdesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
|
qdesc = CreateQueryDesc(query, plan, None_Receiver,
|
||||||
paramLI, stmt->analyze);
|
paramLI, stmt->analyze);
|
||||||
|
|
||||||
ExplainOnePlan(qdesc, stmt, tstate);
|
ExplainOnePlan(qdesc, stmt, tstate);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -217,10 +217,7 @@ ExecutorRun(QueryDesc *queryDesc,
|
|||||||
estate->es_processed = 0;
|
estate->es_processed = 0;
|
||||||
estate->es_lastoid = InvalidOid;
|
estate->es_lastoid = InvalidOid;
|
||||||
|
|
||||||
(*dest->startup) (dest, operation,
|
(*dest->startup) (dest, operation, queryDesc->tupDesc);
|
||||||
queryDesc->portalName,
|
|
||||||
queryDesc->tupDesc,
|
|
||||||
queryDesc->planstate->plan->targetlist);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* run plan
|
* run plan
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -765,8 +765,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
|
|||||||
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
|
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
|
||||||
tstate->dest = dest;
|
tstate->dest = dest;
|
||||||
|
|
||||||
(*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT,
|
(*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, tupdesc);
|
||||||
NULL, tupdesc, NIL);
|
|
||||||
|
|
||||||
return tstate;
|
return tstate;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -245,7 +245,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
|
|||||||
{
|
{
|
||||||
Assert(es->qd == NULL);
|
Assert(es->qd == NULL);
|
||||||
es->qd = CreateQueryDesc(es->query, es->plan,
|
es->qd = CreateQueryDesc(es->query, es->plan,
|
||||||
None_Receiver, NULL,
|
None_Receiver,
|
||||||
fcache->paramLI, false);
|
fcache->paramLI, false);
|
||||||
|
|
||||||
/* Utility commands don't need Executor. */
|
/* Utility commands don't need Executor. */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -841,7 +841,8 @@ SPI_cursor_find(const char *name)
|
|||||||
void
|
void
|
||||||
SPI_cursor_fetch(Portal portal, bool forward, int count)
|
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 */
|
/* we know that the SPI receiver doesn't need a destroy call */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,8 +881,7 @@ SPI_cursor_close(Portal portal)
|
|||||||
* of current SPI procedure
|
* of current SPI procedure
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
spi_dest_startup(DestReceiver *self, int operation,
|
spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
|
||||||
{
|
{
|
||||||
SPITupleTable *tuptable;
|
SPITupleTable *tuptable;
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
@ -1035,7 +1035,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
|||||||
planTree = pg_plan_query(queryTree);
|
planTree = pg_plan_query(queryTree);
|
||||||
plan_list = lappend(plan_list, planTree);
|
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 (queryTree->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
if (IsA(queryTree->utilityStmt, CopyStmt))
|
if (IsA(queryTree->utilityStmt, CopyStmt))
|
||||||
@ -1061,7 +1061,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
|||||||
else if (plan == NULL)
|
else if (plan == NULL)
|
||||||
{
|
{
|
||||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||||
NULL, NULL, false);
|
NULL, false);
|
||||||
res = _SPI_pquery(qdesc, true,
|
res = _SPI_pquery(qdesc, true,
|
||||||
queryTree->canSetTag ? tcount : 0);
|
queryTree->canSetTag ? tcount : 0);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
@ -1071,7 +1071,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||||
NULL, NULL, false);
|
NULL, false);
|
||||||
res = _SPI_pquery(qdesc, false, 0);
|
res = _SPI_pquery(qdesc, false, 0);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return res;
|
return res;
|
||||||
@ -1150,7 +1150,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
planTree = lfirst(plan_list);
|
planTree = lfirst(plan_list);
|
||||||
plan_list = lnext(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)
|
if (queryTree->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
ProcessUtility(queryTree->utilityStmt, dest, NULL);
|
ProcessUtility(queryTree->utilityStmt, dest, NULL);
|
||||||
@ -1160,7 +1160,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
qdesc = CreateQueryDesc(queryTree, planTree, dest,
|
||||||
NULL, paramLI, false);
|
paramLI, false);
|
||||||
res = _SPI_pquery(qdesc, true,
|
res = _SPI_pquery(qdesc, true,
|
||||||
queryTree->canSetTag ? tcount : 0);
|
queryTree->canSetTag ? tcount : 0);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -31,9 +31,7 @@ typedef struct
|
|||||||
* Prepare to receive tuples from executor.
|
* Prepare to receive tuples from executor.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tstoreStartupReceiver(DestReceiver *self, int operation,
|
tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
const char *portalname,
|
|
||||||
TupleDesc typeinfo, List *targetlist)
|
|
||||||
{
|
{
|
||||||
/* do nothing */
|
/* do nothing */
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -104,28 +104,33 @@ pq_sendbytes(StringInfo buf, const char *data, int datalen)
|
|||||||
* pq_sendcountedtext - append a text string (with character set conversion)
|
* 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 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
|
* The passed text string need not be null-terminated, and the data sent
|
||||||
* to the frontend isn't either.
|
* to the frontend isn't either.
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
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;
|
char *p;
|
||||||
|
|
||||||
p = (char *) pg_server_to_client((unsigned char *) str, slen);
|
p = (char *) pg_server_to_client((unsigned char *) str, slen);
|
||||||
if (p != str) /* actual conversion has been done? */
|
if (p != str) /* actual conversion has been done? */
|
||||||
{
|
{
|
||||||
slen = strlen(p);
|
slen = strlen(p);
|
||||||
pq_sendint(buf, slen + 4, 4);
|
pq_sendint(buf, slen + extra, 4);
|
||||||
appendBinaryStringInfo(buf, p, slen);
|
appendBinaryStringInfo(buf, p, slen);
|
||||||
pfree(p);
|
pfree(p);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
pq_sendint(buf, slen + 4, 4);
|
else
|
||||||
|
{
|
||||||
|
pq_sendint(buf, slen + extra, 4);
|
||||||
appendBinaryStringInfo(buf, str, slen);
|
appendBinaryStringInfo(buf, str, slen);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_sendstring - append a null-terminated text string (with conversion)
|
* pq_sendstring - append a null-terminated text string (with conversion)
|
||||||
@ -296,7 +301,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
|
|||||||
{
|
{
|
||||||
const char *result;
|
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");
|
elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
|
||||||
result = &msg->data[msg->cursor];
|
result = &msg->data[msg->cursor];
|
||||||
msg->cursor += datalen;
|
msg->cursor += datalen;
|
||||||
@ -312,7 +317,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
|
|||||||
void
|
void
|
||||||
pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
|
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");
|
elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
|
||||||
memcpy(buf, &msg->data[msg->cursor], datalen);
|
memcpy(buf, &msg->data[msg->cursor], datalen);
|
||||||
msg->cursor += datalen;
|
msg->cursor += datalen;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -30,8 +30,10 @@
|
|||||||
|
|
||||||
#include "access/printtup.h"
|
#include "access/printtup.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
|
#include "executor/tstoreReceiver.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -44,8 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
donothingStartup(DestReceiver *self, int operation,
|
donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,18 +91,21 @@ BeginCommand(const char *commandTag, CommandDest dest)
|
|||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* CreateDestReceiver - return appropriate receiver function set for dest
|
* 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 *
|
DestReceiver *
|
||||||
CreateDestReceiver(CommandDest dest)
|
CreateDestReceiver(CommandDest dest, Portal portal)
|
||||||
{
|
{
|
||||||
switch (dest)
|
switch (dest)
|
||||||
{
|
{
|
||||||
case Remote:
|
case Remote:
|
||||||
case RemoteInternal:
|
|
||||||
case RemoteExecute:
|
case RemoteExecute:
|
||||||
case RemoteExecuteInternal:
|
if (portal == NULL)
|
||||||
return printtup_create_DR(dest);
|
elog(ERROR, "CreateDestReceiver: no portal specified");
|
||||||
|
return printtup_create_DR(dest, portal);
|
||||||
|
|
||||||
case None:
|
case None:
|
||||||
return &donothingDR;
|
return &donothingDR;
|
||||||
@ -113,12 +117,13 @@ CreateDestReceiver(CommandDest dest)
|
|||||||
return &spi_printtupDR;
|
return &spi_printtupDR;
|
||||||
|
|
||||||
case Tuplestore:
|
case Tuplestore:
|
||||||
/*
|
if (portal == NULL)
|
||||||
* This is disallowed, you must use tstoreReceiver.c's
|
elog(ERROR, "CreateDestReceiver: no portal specified");
|
||||||
* specialized function to create a Tuplestore DestReceiver
|
if (portal->holdStore == NULL ||
|
||||||
*/
|
portal->holdContext == NULL)
|
||||||
elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore");
|
elog(ERROR, "CreateDestReceiver: portal has no holdStore");
|
||||||
break;
|
return CreateTuplestoreDestReceiver(portal->holdStore,
|
||||||
|
portal->holdContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* should never get here */
|
/* should never get here */
|
||||||
@ -135,9 +140,7 @@ EndCommand(const char *commandTag, CommandDest dest)
|
|||||||
switch (dest)
|
switch (dest)
|
||||||
{
|
{
|
||||||
case Remote:
|
case Remote:
|
||||||
case RemoteInternal:
|
|
||||||
case RemoteExecute:
|
case RemoteExecute:
|
||||||
case RemoteExecuteInternal:
|
|
||||||
pq_puttextmessage('C', commandTag);
|
pq_puttextmessage('C', commandTag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -167,9 +170,7 @@ NullCommand(CommandDest dest)
|
|||||||
switch (dest)
|
switch (dest)
|
||||||
{
|
{
|
||||||
case Remote:
|
case Remote:
|
||||||
case RemoteInternal:
|
|
||||||
case RemoteExecute:
|
case RemoteExecute:
|
||||||
case RemoteExecuteInternal:
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tell the fe that we saw an empty query string. In protocols
|
* tell the fe that we saw an empty query string. In protocols
|
||||||
@ -206,9 +207,7 @@ ReadyForQuery(CommandDest dest)
|
|||||||
switch (dest)
|
switch (dest)
|
||||||
{
|
{
|
||||||
case Remote:
|
case Remote:
|
||||||
case RemoteInternal:
|
|
||||||
case RemoteExecute:
|
case RemoteExecute:
|
||||||
case RemoteExecuteInternal:
|
|
||||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* This cruft is the server side of PQfn.
|
* This cruft is the server side of PQfn.
|
||||||
@ -47,6 +47,34 @@
|
|||||||
#include "utils/tqual.h"
|
#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
|
* GetOldFunctionMessage
|
||||||
*
|
*
|
||||||
@ -121,6 +149,42 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
|
|||||||
|
|
||||||
pq_beginmessage(&buf, 'V');
|
pq_beginmessage(&buf, 'V');
|
||||||
|
|
||||||
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||||
|
{
|
||||||
|
/* 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
|
||||||
|
{
|
||||||
|
/* NULL marker */
|
||||||
|
pq_sendint(&buf, -1, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Old-style message */
|
||||||
if (retlen != 0)
|
if (retlen != 0)
|
||||||
{
|
{
|
||||||
pq_sendbyte(&buf, 'G');
|
pq_sendbyte(&buf, 'G');
|
||||||
@ -145,31 +209,11 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pq_sendbyte(&buf, '0');
|
pq_sendbyte(&buf, '0');
|
||||||
pq_endmessage(&buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
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
|
* fetch_fp_info
|
||||||
@ -262,11 +306,9 @@ int
|
|||||||
HandleFunctionRequest(StringInfo msgBuf)
|
HandleFunctionRequest(StringInfo msgBuf)
|
||||||
{
|
{
|
||||||
Oid fid;
|
Oid fid;
|
||||||
int nargs;
|
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
FunctionCallInfoData fcinfo;
|
FunctionCallInfoData fcinfo;
|
||||||
Datum retval;
|
Datum retval;
|
||||||
int i;
|
|
||||||
struct fp_info my_fp;
|
struct fp_info my_fp;
|
||||||
struct fp_info *fip;
|
struct fp_info *fip;
|
||||||
|
|
||||||
@ -294,9 +336,10 @@ HandleFunctionRequest(StringInfo msgBuf)
|
|||||||
/*
|
/*
|
||||||
* Parse the buffer contents.
|
* Parse the buffer contents.
|
||||||
*/
|
*/
|
||||||
|
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
|
||||||
(void) pq_getmsgstring(msgBuf); /* dummy string */
|
(void) pq_getmsgstring(msgBuf); /* dummy string */
|
||||||
|
|
||||||
fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
|
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
|
* There used to be a lame attempt at caching lookup info here. Now we
|
||||||
@ -316,19 +359,61 @@ HandleFunctionRequest(StringInfo msgBuf)
|
|||||||
SetQuerySnapshot();
|
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)
|
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
|
||||||
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
|
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
|
||||||
nargs, fip->flinfo.fn_nargs);
|
nargs, fip->flinfo.fn_nargs);
|
||||||
|
|
||||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
fcinfo->nargs = nargs;
|
||||||
fcinfo.flinfo = &fip->flinfo;
|
|
||||||
fcinfo.nargs = nargs;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy supplied arguments into arg vector. Note there is no way for
|
* Copy supplied arguments into arg vector.
|
||||||
* frontend to specify a NULL argument --- this protocol is misdesigned.
|
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < nargs; ++i)
|
for (i = 0; i < nargs; ++i)
|
||||||
{
|
{
|
||||||
@ -342,7 +427,7 @@ HandleFunctionRequest(StringInfo msgBuf)
|
|||||||
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
||||||
argsize);
|
argsize);
|
||||||
/* XXX should we demand argsize == fip->arglen[i] ? */
|
/* 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
|
else
|
||||||
{ /* by-reference ... */
|
{ /* by-reference ... */
|
||||||
@ -363,25 +448,70 @@ HandleFunctionRequest(StringInfo msgBuf)
|
|||||||
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
|
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
|
||||||
pq_copymsgbytes(msgBuf, p, argsize);
|
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. */
|
/* XXX for the moment, ignore result format code */
|
||||||
pq_getmsgend(msgBuf);
|
(void) pq_getmsgint(msgBuf, 2);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef NO_FASTPATH
|
/*
|
||||||
/* force a NULL return */
|
* Parse function arguments in a 2.0 protocol message
|
||||||
retval = (Datum) 0;
|
*/
|
||||||
fcinfo.isnull = true;
|
static void
|
||||||
#else
|
parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
|
||||||
retval = FunctionCallInvoke(&fcinfo);
|
FunctionCallInfo fcinfo)
|
||||||
#endif /* NO_FASTPATH */
|
{
|
||||||
|
int nargs;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (fcinfo.isnull)
|
nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
|
||||||
SendFunctionResult(retval, fip->retbyval, 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
|
else
|
||||||
SendFunctionResult(retval, fip->retbyval, fip->retlen);
|
{ /* by-reference ... */
|
||||||
|
if (fip->arglen[i] == -1)
|
||||||
return 0;
|
{ /* ... 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -658,7 +658,6 @@ static void
|
|||||||
exec_simple_query(const char *query_string)
|
exec_simple_query(const char *query_string)
|
||||||
{
|
{
|
||||||
CommandDest dest = whereToSendOutput;
|
CommandDest dest = whereToSendOutput;
|
||||||
DestReceiver *receiver;
|
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
List *parsetree_list,
|
List *parsetree_list,
|
||||||
*parsetree_item;
|
*parsetree_item;
|
||||||
@ -685,12 +684,6 @@ exec_simple_query(const char *query_string)
|
|||||||
if (save_log_statement_stats)
|
if (save_log_statement_stats)
|
||||||
ResetUsage();
|
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
|
* Start up a transaction command. All queries generated by the
|
||||||
* query_string will be in this same command block, *unless* we find a
|
* query_string will be in this same command block, *unless* we find a
|
||||||
@ -743,6 +736,8 @@ exec_simple_query(const char *query_string)
|
|||||||
List *querytree_list,
|
List *querytree_list,
|
||||||
*plantree_list;
|
*plantree_list;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
|
DestReceiver *receiver;
|
||||||
|
int16 format;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the command name for use in status display (it also becomes the
|
* Get the command name for use in status display (it also becomes the
|
||||||
@ -803,11 +798,6 @@ exec_simple_query(const char *query_string)
|
|||||||
/* If we got a cancel signal in analysis or planning, quit */
|
/* If we got a cancel signal in analysis or planning, quit */
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/*
|
|
||||||
* Switch back to transaction context for execution.
|
|
||||||
*/
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create unnamed portal to run the query or queries in.
|
* Create unnamed portal to run the query or queries in.
|
||||||
* If there already is one, silently drop it.
|
* If there already is one, silently drop it.
|
||||||
@ -822,16 +812,53 @@ exec_simple_query(const char *query_string)
|
|||||||
MessageContext);
|
MessageContext);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Run the portal to completion, and then drop it.
|
* Start the portal. No parameters here.
|
||||||
*/
|
*/
|
||||||
PortalStart(portal, NULL);
|
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,
|
(void) PortalRun(portal,
|
||||||
FETCH_ALL,
|
FETCH_ALL,
|
||||||
receiver,
|
receiver,
|
||||||
receiver,
|
receiver,
|
||||||
completionTag);
|
completionTag);
|
||||||
|
|
||||||
|
(*receiver->destroy) (receiver);
|
||||||
|
|
||||||
PortalDrop(portal, false);
|
PortalDrop(portal, false);
|
||||||
|
|
||||||
|
|
||||||
@ -886,8 +913,6 @@ exec_simple_query(const char *query_string)
|
|||||||
if (!parsetree_list)
|
if (!parsetree_list)
|
||||||
NullCommand(dest);
|
NullCommand(dest);
|
||||||
|
|
||||||
(*receiver->destroy) (receiver);
|
|
||||||
|
|
||||||
QueryContext = NULL;
|
QueryContext = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1156,8 +1181,12 @@ exec_bind_message(StringInfo input_message)
|
|||||||
{
|
{
|
||||||
const char *portal_name;
|
const char *portal_name;
|
||||||
const char *stmt_name;
|
const char *stmt_name;
|
||||||
int is_binary;
|
int numPFormats;
|
||||||
|
int16 *pformats = NULL;
|
||||||
int numParams;
|
int numParams;
|
||||||
|
int numRFormats;
|
||||||
|
int16 *rformats = NULL;
|
||||||
|
int i;
|
||||||
PreparedStatement *pstmt;
|
PreparedStatement *pstmt;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
ParamListInfo params;
|
ParamListInfo params;
|
||||||
@ -1173,14 +1202,28 @@ exec_bind_message(StringInfo input_message)
|
|||||||
*/
|
*/
|
||||||
start_xact_command();
|
start_xact_command();
|
||||||
|
|
||||||
|
/* Switch back to message context */
|
||||||
|
MemoryContextSwitchTo(MessageContext);
|
||||||
|
|
||||||
/* Get the fixed part of the message */
|
/* Get the fixed part of the message */
|
||||||
portal_name = pq_getmsgstring(input_message);
|
portal_name = pq_getmsgstring(input_message);
|
||||||
stmt_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)
|
/* Get the parameter format codes */
|
||||||
elog(ERROR, "Binary BIND not implemented yet");
|
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 */
|
/* Find prepared statement */
|
||||||
if (stmt_name[0] != '\0')
|
if (stmt_name[0] != '\0')
|
||||||
@ -1217,45 +1260,98 @@ exec_bind_message(StringInfo input_message)
|
|||||||
* Fetch parameters, if any, and store in the portal's memory context.
|
* Fetch parameters, if any, and store in the portal's memory context.
|
||||||
*
|
*
|
||||||
* In an aborted transaction, we can't risk calling user-defined functions,
|
* 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)
|
if (numParams > 0)
|
||||||
{
|
{
|
||||||
bool isaborted = IsAbortedTransactionBlockState();
|
bool isaborted = IsAbortedTransactionBlockState();
|
||||||
int i = 0;
|
StringInfoData pbuf;
|
||||||
List *l;
|
List *l;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
|
||||||
|
/* Note that the string buffer lives in MessageContext */
|
||||||
|
initStringInfo(&pbuf);
|
||||||
|
|
||||||
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
|
|
||||||
params = (ParamListInfo)
|
params = (ParamListInfo)
|
||||||
palloc0((numParams + 1) * sizeof(ParamListInfoData));
|
palloc0((numParams + 1) * sizeof(ParamListInfoData));
|
||||||
|
|
||||||
|
i = 0;
|
||||||
foreach(l, pstmt->argtype_list)
|
foreach(l, pstmt->argtype_list)
|
||||||
{
|
{
|
||||||
Oid ptype = lfirsto(l);
|
Oid ptype = lfirsto(l);
|
||||||
|
int32 plength;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
|
|
||||||
isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
|
plength = pq_getmsgint(input_message, 4);
|
||||||
|
isNull = (plength == -1);
|
||||||
|
|
||||||
if (!isNull)
|
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)
|
if (isaborted)
|
||||||
|
{
|
||||||
|
/* We don't bother to check the format in this case */
|
||||||
isNull = true;
|
isNull = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
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 typInput;
|
||||||
Oid typElem;
|
Oid typElem;
|
||||||
|
char *pstring;
|
||||||
|
|
||||||
getTypeInputInfo(ptype, &typInput, &typElem);
|
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 =
|
params[i].value =
|
||||||
OidFunctionCall3(typInput,
|
OidFunctionCall3(typInput,
|
||||||
CStringGetDatum(ptext),
|
CStringGetDatum(pstring),
|
||||||
ObjectIdGetDatum(typElem),
|
ObjectIdGetDatum(typElem),
|
||||||
Int32GetDatum(-1));
|
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].kind = PARAM_NUM;
|
||||||
params[i].id = i + 1;
|
params[i].id = i + 1;
|
||||||
params[i].isnull = isNull;
|
params[i].isnull = isNull;
|
||||||
@ -1270,6 +1366,15 @@ exec_bind_message(StringInfo input_message)
|
|||||||
else
|
else
|
||||||
params = NULL;
|
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);
|
pq_getmsgend(input_message);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1277,6 +1382,11 @@ exec_bind_message(StringInfo input_message)
|
|||||||
*/
|
*/
|
||||||
PortalStart(portal, params);
|
PortalStart(portal, params);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply the result format requests to the portal.
|
||||||
|
*/
|
||||||
|
PortalSetResultFormat(portal, numRFormats, rformats);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send BindComplete.
|
* Send BindComplete.
|
||||||
*/
|
*/
|
||||||
@ -1290,7 +1400,7 @@ exec_bind_message(StringInfo input_message)
|
|||||||
* Process an "Execute" message for a portal
|
* Process an "Execute" message for a portal
|
||||||
*/
|
*/
|
||||||
static void
|
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;
|
CommandDest dest;
|
||||||
DestReceiver *receiver;
|
DestReceiver *receiver;
|
||||||
@ -1303,7 +1413,7 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
|
|||||||
/* Adjust destination to tell printtup.c what to do */
|
/* Adjust destination to tell printtup.c what to do */
|
||||||
dest = whereToSendOutput;
|
dest = whereToSendOutput;
|
||||||
if (dest == Remote)
|
if (dest == Remote)
|
||||||
dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
|
dest = RemoteExecute;
|
||||||
|
|
||||||
portal = GetPortalByName(portal_name);
|
portal = GetPortalByName(portal_name);
|
||||||
if (!PortalIsValid(portal))
|
if (!PortalIsValid(portal))
|
||||||
@ -1352,6 +1462,12 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Ensure we are in a transaction command (this should normally be
|
||||||
* the case already due to prior BIND).
|
* the case already due to prior BIND).
|
||||||
@ -1375,8 +1491,6 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
|
|||||||
/*
|
/*
|
||||||
* Okay to run the portal.
|
* Okay to run the portal.
|
||||||
*/
|
*/
|
||||||
receiver = CreateDestReceiver(dest);
|
|
||||||
|
|
||||||
if (max_rows <= 0)
|
if (max_rows <= 0)
|
||||||
max_rows = FETCH_ALL;
|
max_rows = FETCH_ALL;
|
||||||
|
|
||||||
@ -1451,7 +1565,7 @@ exec_describe_statement_message(const char *stmt_name)
|
|||||||
* First describe the parameters...
|
* First describe the parameters...
|
||||||
*/
|
*/
|
||||||
pq_beginmessage(&buf, 't'); /* parameter description message type */
|
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)
|
foreach(l, pstmt->argtype_list)
|
||||||
{
|
{
|
||||||
@ -1473,7 +1587,7 @@ exec_describe_statement_message(const char *stmt_name)
|
|||||||
targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
|
targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
|
||||||
else
|
else
|
||||||
targetlist = NIL;
|
targetlist = NIL;
|
||||||
SendRowDescriptionMessage(tupdesc, targetlist);
|
SendRowDescriptionMessage(tupdesc, targetlist, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pq_putemptymessage('n'); /* NoData */
|
pq_putemptymessage('n'); /* NoData */
|
||||||
@ -1502,10 +1616,11 @@ exec_describe_portal_message(const char *portal_name)
|
|||||||
List *targetlist;
|
List *targetlist;
|
||||||
|
|
||||||
if (portal->strategy == PORTAL_ONE_SELECT)
|
if (portal->strategy == PORTAL_ONE_SELECT)
|
||||||
targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
|
targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
|
||||||
else
|
else
|
||||||
targetlist = NIL;
|
targetlist = NIL;
|
||||||
SendRowDescriptionMessage(portal->tupDesc, targetlist);
|
SendRowDescriptionMessage(portal->tupDesc, targetlist,
|
||||||
|
portal->formats);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pq_putemptymessage('n'); /* NoData */
|
pq_putemptymessage('n'); /* NoData */
|
||||||
@ -2397,7 +2512,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
if (!IsUnderPostmaster)
|
if (!IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
puts("\nPOSTGRES backend interactive interface ");
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2613,7 +2728,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
|
|
||||||
stmt_name = pq_getmsgstring(input_message);
|
stmt_name = pq_getmsgstring(input_message);
|
||||||
query_string = 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)
|
if (numParams > 0)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -2640,15 +2755,13 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
case 'E': /* execute */
|
case 'E': /* execute */
|
||||||
{
|
{
|
||||||
const char *portal_name;
|
const char *portal_name;
|
||||||
int is_binary;
|
|
||||||
int max_rows;
|
int max_rows;
|
||||||
|
|
||||||
portal_name = pq_getmsgstring(input_message);
|
portal_name = pq_getmsgstring(input_message);
|
||||||
is_binary = pq_getmsgbyte(input_message);
|
|
||||||
max_rows = pq_getmsgint(input_message, 4);
|
max_rows = pq_getmsgint(input_message, 4);
|
||||||
pq_getmsgend(input_message);
|
pq_getmsgend(input_message);
|
||||||
|
|
||||||
exec_execute_message(portal_name, is_binary, max_rows);
|
exec_execute_message(portal_name, max_rows);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -8,14 +8,13 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/tstoreReceiver.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
@ -47,7 +46,6 @@ QueryDesc *
|
|||||||
CreateQueryDesc(Query *parsetree,
|
CreateQueryDesc(Query *parsetree,
|
||||||
Plan *plantree,
|
Plan *plantree,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
const char *portalName,
|
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
bool doInstrument)
|
bool doInstrument)
|
||||||
{
|
{
|
||||||
@ -57,7 +55,6 @@ CreateQueryDesc(Query *parsetree,
|
|||||||
qd->parsetree = parsetree; /* parse tree */
|
qd->parsetree = parsetree; /* parse tree */
|
||||||
qd->plantree = plantree; /* plan */
|
qd->plantree = plantree; /* plan */
|
||||||
qd->dest = dest; /* output dest */
|
qd->dest = dest; /* output dest */
|
||||||
qd->portalName = portalName; /* name, if dest is a portal */
|
|
||||||
qd->params = params; /* parameter values passed into query */
|
qd->params = params; /* parameter values passed into query */
|
||||||
qd->doInstrument = doInstrument; /* instrumentation wanted? */
|
qd->doInstrument = doInstrument; /* instrumentation wanted? */
|
||||||
|
|
||||||
@ -89,7 +86,6 @@ FreeQueryDesc(QueryDesc *qdesc)
|
|||||||
* parsetree: the query tree
|
* parsetree: the query tree
|
||||||
* plan: the plan tree for the query
|
* plan: the plan tree for the query
|
||||||
* params: any parameters needed
|
* params: any parameters needed
|
||||||
* portalName: name of portal being used
|
|
||||||
* dest: where to send results
|
* dest: where to send results
|
||||||
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
||||||
* in which to store a command completion status string.
|
* in which to store a command completion status string.
|
||||||
@ -103,7 +99,6 @@ void
|
|||||||
ProcessQuery(Query *parsetree,
|
ProcessQuery(Query *parsetree,
|
||||||
Plan *plan,
|
Plan *plan,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
const char *portalName,
|
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
@ -131,8 +126,7 @@ ProcessQuery(Query *parsetree,
|
|||||||
/*
|
/*
|
||||||
* Create the QueryDesc object
|
* Create the QueryDesc object
|
||||||
*/
|
*/
|
||||||
queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params,
|
queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
|
||||||
false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call ExecStart to prepare the plan for execution
|
* Call ExecStart to prepare the plan for execution
|
||||||
@ -269,7 +263,6 @@ PortalStart(Portal portal, ParamListInfo params)
|
|||||||
queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
|
queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
|
||||||
(Plan *) lfirst(portal->planTrees),
|
(Plan *) lfirst(portal->planTrees),
|
||||||
None_Receiver,
|
None_Receiver,
|
||||||
portal->name,
|
|
||||||
params,
|
params,
|
||||||
false);
|
false);
|
||||||
/*
|
/*
|
||||||
@ -281,7 +274,7 @@ PortalStart(Portal portal, ParamListInfo params)
|
|||||||
*/
|
*/
|
||||||
portal->queryDesc = queryDesc;
|
portal->queryDesc = queryDesc;
|
||||||
/*
|
/*
|
||||||
* Remember tuple descriptor
|
* Remember tuple descriptor (computed by ExecutorStart)
|
||||||
*/
|
*/
|
||||||
portal->tupDesc = queryDesc->tupDesc;
|
portal->tupDesc = queryDesc->tupDesc;
|
||||||
/*
|
/*
|
||||||
@ -320,6 +313,53 @@ PortalStart(Portal portal, ParamListInfo params)
|
|||||||
portal->portalReady = true;
|
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
|
* PortalRun
|
||||||
* Run a portal's query or queries.
|
* Run a portal's query or queries.
|
||||||
@ -399,8 +439,7 @@ PortalRun(Portal portal, long count,
|
|||||||
DestReceiver *treceiver;
|
DestReceiver *treceiver;
|
||||||
|
|
||||||
PortalCreateHoldStore(portal);
|
PortalCreateHoldStore(portal);
|
||||||
treceiver = CreateTuplestoreDestReceiver(portal->holdStore,
|
treceiver = CreateDestReceiver(Tuplestore, portal);
|
||||||
portal->holdContext);
|
|
||||||
PortalRunUtility(portal, lfirst(portal->parseTrees),
|
PortalRunUtility(portal, lfirst(portal->parseTrees),
|
||||||
treceiver, NULL);
|
treceiver, NULL);
|
||||||
(*treceiver->destroy) (treceiver);
|
(*treceiver->destroy) (treceiver);
|
||||||
@ -604,16 +643,9 @@ static uint32
|
|||||||
RunFromStore(Portal portal, ScanDirection direction, long count,
|
RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||||
DestReceiver *dest)
|
DestReceiver *dest)
|
||||||
{
|
{
|
||||||
List *targetlist;
|
|
||||||
long current_tuple_count = 0;
|
long current_tuple_count = 0;
|
||||||
|
|
||||||
if (portal->strategy == PORTAL_ONE_SELECT)
|
(*dest->startup) (dest, CMD_SELECT, portal->tupDesc);
|
||||||
targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
|
|
||||||
else
|
|
||||||
targetlist = NIL;
|
|
||||||
|
|
||||||
(*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc,
|
|
||||||
targetlist);
|
|
||||||
|
|
||||||
if (direction == NoMovementScanDirection)
|
if (direction == NoMovementScanDirection)
|
||||||
{
|
{
|
||||||
@ -737,11 +769,9 @@ PortalRunMulti(Portal portal,
|
|||||||
* but the results will be discarded unless you use "simple Query"
|
* but the results will be discarded unless you use "simple Query"
|
||||||
* protocol.
|
* protocol.
|
||||||
*/
|
*/
|
||||||
if (dest->mydest == RemoteExecute ||
|
if (dest->mydest == RemoteExecute)
|
||||||
dest->mydest == RemoteExecuteInternal)
|
|
||||||
dest = None_Receiver;
|
dest = None_Receiver;
|
||||||
if (altdest->mydest == RemoteExecute ||
|
if (altdest->mydest == RemoteExecute)
|
||||||
altdest->mydest == RemoteExecuteInternal)
|
|
||||||
altdest = None_Receiver;
|
altdest = None_Receiver;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -791,14 +821,14 @@ PortalRunMulti(Portal portal,
|
|||||||
{
|
{
|
||||||
/* statement can set tag string */
|
/* statement can set tag string */
|
||||||
ProcessQuery(query, plan,
|
ProcessQuery(query, plan,
|
||||||
portal->portalParams, portal->name,
|
portal->portalParams,
|
||||||
dest, completionTag);
|
dest, completionTag);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* stmt added by rewrite cannot set tag */
|
/* stmt added by rewrite cannot set tag */
|
||||||
ProcessQuery(query, plan,
|
ProcessQuery(query, plan,
|
||||||
portal->portalParams, portal->name,
|
portal->portalParams,
|
||||||
altdest, NULL);
|
altdest, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,18 +16,19 @@
|
|||||||
|
|
||||||
#include "tcop/dest.h"
|
#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,
|
extern void debugStartup(DestReceiver *self, int operation,
|
||||||
const char *portalName, TupleDesc typeinfo, List *targetlist);
|
TupleDesc typeinfo);
|
||||||
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
|
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||||
DestReceiver *self);
|
DestReceiver *self);
|
||||||
|
|
||||||
/* XXX these are really in executor/spi.c */
|
/* XXX these are really in executor/spi.c */
|
||||||
extern void spi_dest_startup(DestReceiver *self, int operation,
|
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,
|
extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||||
DestReceiver *self);
|
DestReceiver *self);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -34,7 +34,6 @@ typedef struct QueryDesc
|
|||||||
Query *parsetree; /* rewritten parsetree */
|
Query *parsetree; /* rewritten parsetree */
|
||||||
Plan *plantree; /* planner's output */
|
Plan *plantree; /* planner's output */
|
||||||
DestReceiver *dest; /* the destination for tuple output */
|
DestReceiver *dest; /* the destination for tuple output */
|
||||||
const char *portalName; /* name of portal, or NULL */
|
|
||||||
ParamListInfo params; /* param values being passed in */
|
ParamListInfo params; /* param values being passed in */
|
||||||
bool doInstrument; /* TRUE requests runtime instrumentation */
|
bool doInstrument; /* TRUE requests runtime instrumentation */
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ typedef struct QueryDesc
|
|||||||
|
|
||||||
/* in pquery.c */
|
/* in pquery.c */
|
||||||
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
|
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
|
||||||
DestReceiver *dest, const char *portalName,
|
DestReceiver *dest,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
bool doInstrument);
|
bool doInstrument);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -106,7 +106,7 @@ typedef union SockAddr
|
|||||||
/* The earliest and latest frontend/backend protocol version supported. */
|
/* The earliest and latest frontend/backend protocol version supported. */
|
||||||
|
|
||||||
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
|
#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 */
|
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -18,7 +18,8 @@
|
|||||||
extern void pq_beginmessage(StringInfo buf, char msgtype);
|
extern void pq_beginmessage(StringInfo buf, char msgtype);
|
||||||
extern void pq_sendbyte(StringInfo buf, int byt);
|
extern void pq_sendbyte(StringInfo buf, int byt);
|
||||||
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
|
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_sendstring(StringInfo buf, const char *str);
|
||||||
extern void pq_sendint(StringInfo buf, int i, int b);
|
extern void pq_sendint(StringInfo buf, int i, int b);
|
||||||
extern void pq_endmessage(StringInfo buf);
|
extern void pq_endmessage(StringInfo buf);
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -82,12 +82,9 @@ typedef enum
|
|||||||
None, /* results are discarded */
|
None, /* results are discarded */
|
||||||
Debug, /* results go to debugging output */
|
Debug, /* results go to debugging output */
|
||||||
Remote, /* results sent to frontend process */
|
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 */
|
RemoteExecute, /* sent to frontend, in Execute command */
|
||||||
RemoteExecuteInternal /* same, but binary format */
|
SPI, /* results sent to SPI manager */
|
||||||
|
Tuplestore /* results sent to Tuplestore */
|
||||||
} CommandDest;
|
} CommandDest;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -106,13 +103,13 @@ typedef struct _DestReceiver DestReceiver;
|
|||||||
struct _DestReceiver
|
struct _DestReceiver
|
||||||
{
|
{
|
||||||
/* Called for each tuple to be output: */
|
/* Called for each tuple to be output: */
|
||||||
void (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
|
void (*receiveTuple) (HeapTuple tuple,
|
||||||
|
TupleDesc typeinfo,
|
||||||
DestReceiver *self);
|
DestReceiver *self);
|
||||||
/* Per-executor-run initialization and shutdown: */
|
/* Per-executor-run initialization and shutdown: */
|
||||||
void (*startup) (DestReceiver *self, int operation,
|
void (*startup) (DestReceiver *self,
|
||||||
const char *portalName,
|
int operation,
|
||||||
TupleDesc typeinfo,
|
TupleDesc typeinfo);
|
||||||
List *targetlist);
|
|
||||||
void (*shutdown) (DestReceiver *self);
|
void (*shutdown) (DestReceiver *self);
|
||||||
/* Destroy the receiver object itself (if dynamically allocated) */
|
/* Destroy the receiver object itself (if dynamically allocated) */
|
||||||
void (*destroy) (DestReceiver *self);
|
void (*destroy) (DestReceiver *self);
|
||||||
@ -123,10 +120,14 @@ struct _DestReceiver
|
|||||||
|
|
||||||
extern DestReceiver *None_Receiver; /* permanent receiver for None */
|
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 */
|
/* The primary destination management functions */
|
||||||
|
|
||||||
extern void BeginCommand(const char *commandTag, CommandDest dest);
|
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);
|
extern void EndCommand(const char *commandTag, CommandDest dest);
|
||||||
|
|
||||||
/* Additional functions that go with destination management, more or less. */
|
/* Additional functions that go with destination management, more or less. */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -20,7 +20,6 @@
|
|||||||
extern void ProcessQuery(Query *parsetree,
|
extern void ProcessQuery(Query *parsetree,
|
||||||
Plan *plan,
|
Plan *plan,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
const char *portalName,
|
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
|
|
||||||
@ -28,6 +27,9 @@ extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
|
|||||||
|
|
||||||
extern void PortalStart(Portal portal, ParamListInfo params);
|
extern void PortalStart(Portal portal, ParamListInfo params);
|
||||||
|
|
||||||
|
extern void PortalSetResultFormat(Portal portal, int nFormats,
|
||||||
|
int16 *formats);
|
||||||
|
|
||||||
extern bool PortalRun(Portal portal, long count,
|
extern bool PortalRun(Portal portal, long count,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -79,7 +79,10 @@ typedef enum PortalStrategy
|
|||||||
PORTAL_MULTI_QUERY
|
PORTAL_MULTI_QUERY
|
||||||
} PortalStrategy;
|
} PortalStrategy;
|
||||||
|
|
||||||
typedef struct PortalData *Portal;
|
/*
|
||||||
|
* Note: typedef Portal is declared in tcop/dest.h as
|
||||||
|
* typedef struct PortalData *Portal;
|
||||||
|
*/
|
||||||
|
|
||||||
typedef struct PortalData
|
typedef struct PortalData
|
||||||
{
|
{
|
||||||
@ -119,6 +122,8 @@ typedef struct PortalData
|
|||||||
|
|
||||||
/* If portal returns tuples, this is their tupdesc: */
|
/* If portal returns tuples, this is their tupdesc: */
|
||||||
TupleDesc tupDesc; /* descriptor for result tuples */
|
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.
|
* Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -55,250 +55,10 @@ static void parseInput(PGconn *conn);
|
|||||||
static void handleSendFailure(PGconn *conn);
|
static void handleSendFailure(PGconn *conn);
|
||||||
static void handleSyncLoss(PGconn *conn, char id, int msgLength);
|
static void handleSyncLoss(PGconn *conn, char id, int msgLength);
|
||||||
static int getRowDescriptions(PGconn *conn);
|
static int getRowDescriptions(PGconn *conn);
|
||||||
static int getAnotherTuple(PGconn *conn, int binary);
|
static int getAnotherTuple(PGconn *conn);
|
||||||
static int getParameterStatus(PGconn *conn);
|
static int getParameterStatus(PGconn *conn);
|
||||||
static int getNotify(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.
|
* Space management for PGresult.
|
||||||
@ -909,7 +669,7 @@ parseInput(PGconn *conn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msgLength > 30000 &&
|
if (msgLength > 30000 &&
|
||||||
!(id == 'T' || id == 'D' || id == 'B' || id == 'd'))
|
!(id == 'T' || id == 'D' || id == 'd'))
|
||||||
{
|
{
|
||||||
handleSyncLoss(conn, id, msgLength);
|
handleSyncLoss(conn, id, msgLength);
|
||||||
return;
|
return;
|
||||||
@ -1074,11 +834,11 @@ parseInput(PGconn *conn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'D': /* ASCII data tuple */
|
case 'D': /* Data Row */
|
||||||
if (conn->result != NULL)
|
if (conn->result != NULL)
|
||||||
{
|
{
|
||||||
/* Read another tuple of a normal query response */
|
/* Read another tuple of a normal query response */
|
||||||
if (getAnotherTuple(conn, FALSE))
|
if (getAnotherTuple(conn))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1090,30 +850,18 @@ parseInput(PGconn *conn)
|
|||||||
conn->inCursor += msgLength;
|
conn->inCursor += msgLength;
|
||||||
}
|
}
|
||||||
break;
|
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 */
|
case 'G': /* Start Copy In */
|
||||||
if (pqGetc(&conn->copy_is_binary, conn))
|
if (pqGetc(&conn->copy_is_binary, conn))
|
||||||
return;
|
return;
|
||||||
|
/* XXX we currently ignore the rest of the message */
|
||||||
|
conn->inCursor = conn->inStart + 5 + msgLength;
|
||||||
conn->asyncStatus = PGASYNC_COPY_IN;
|
conn->asyncStatus = PGASYNC_COPY_IN;
|
||||||
break;
|
break;
|
||||||
case 'H': /* Start Copy Out */
|
case 'H': /* Start Copy Out */
|
||||||
if (pqGetc(&conn->copy_is_binary, conn))
|
if (pqGetc(&conn->copy_is_binary, conn))
|
||||||
return;
|
return;
|
||||||
|
/* XXX we currently ignore the rest of the message */
|
||||||
|
conn->inCursor = conn->inStart + 5 + msgLength;
|
||||||
conn->asyncStatus = PGASYNC_COPY_OUT;
|
conn->asyncStatus = PGASYNC_COPY_OUT;
|
||||||
conn->copy_already_done = 0;
|
conn->copy_already_done = 0;
|
||||||
break;
|
break;
|
||||||
@ -1229,13 +977,15 @@ getRowDescriptions(PGconn *conn)
|
|||||||
int typid;
|
int typid;
|
||||||
int typlen;
|
int typlen;
|
||||||
int atttypmod;
|
int atttypmod;
|
||||||
|
int format;
|
||||||
|
|
||||||
if (pqGets(&conn->workBuffer, conn) ||
|
if (pqGets(&conn->workBuffer, conn) ||
|
||||||
pqGetInt(&tableid, 4, conn) ||
|
pqGetInt(&tableid, 4, conn) ||
|
||||||
pqGetInt(&columnid, 2, conn) ||
|
pqGetInt(&columnid, 2, conn) ||
|
||||||
pqGetInt(&typid, 4, conn) ||
|
pqGetInt(&typid, 4, conn) ||
|
||||||
pqGetInt(&typlen, 2, conn) ||
|
pqGetInt(&typlen, 2, conn) ||
|
||||||
pqGetInt(&atttypmod, 4, conn))
|
pqGetInt(&atttypmod, 4, conn) ||
|
||||||
|
pqGetInt(&format, 2, conn))
|
||||||
{
|
{
|
||||||
PQclear(result);
|
PQclear(result);
|
||||||
return EOF;
|
return EOF;
|
||||||
@ -1247,13 +997,14 @@ getRowDescriptions(PGconn *conn)
|
|||||||
*/
|
*/
|
||||||
columnid = (int) ((int16) columnid);
|
columnid = (int) ((int16) columnid);
|
||||||
typlen = (int) ((int16) typlen);
|
typlen = (int) ((int16) typlen);
|
||||||
|
format = (int) ((int16) format);
|
||||||
|
|
||||||
result->attDescs[i].name = pqResultStrdup(result,
|
result->attDescs[i].name = pqResultStrdup(result,
|
||||||
conn->workBuffer.data);
|
conn->workBuffer.data);
|
||||||
result->attDescs[i].typid = typid;
|
result->attDescs[i].typid = typid;
|
||||||
result->attDescs[i].typlen = typlen;
|
result->attDescs[i].typlen = typlen;
|
||||||
result->attDescs[i].atttypmod = atttypmod;
|
result->attDescs[i].atttypmod = atttypmod;
|
||||||
/* XXX todo: save tableid/columnid too */
|
/* XXX todo: save tableid/columnid, format too */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success! */
|
/* Success! */
|
||||||
@ -1262,7 +1013,7 @@ getRowDescriptions(PGconn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
* We add another tuple to the existing PGresult structure.
|
||||||
* Returns: 0 if completed message, EOF if error or not enough data yet.
|
* Returns: 0 if completed message, EOF if error or not enough data yet.
|
||||||
*
|
*
|
||||||
@ -1272,23 +1023,14 @@ getRowDescriptions(PGconn *conn)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
getAnotherTuple(PGconn *conn, int binary)
|
getAnotherTuple(PGconn *conn)
|
||||||
{
|
{
|
||||||
PGresult *result = conn->result;
|
PGresult *result = conn->result;
|
||||||
int nfields = result->numAttributes;
|
int nfields = result->numAttributes;
|
||||||
PGresAttValue *tup;
|
PGresAttValue *tup;
|
||||||
|
int tupnfields; /* # fields from tuple */
|
||||||
/* 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 vlen; /* length of the current field value */
|
int vlen; /* length of the current field value */
|
||||||
|
int i;
|
||||||
result->binary = binary;
|
|
||||||
|
|
||||||
/* Allocate tuple space if first time for this data message */
|
/* Allocate tuple space if first time for this data message */
|
||||||
if (conn->curTuple == NULL)
|
if (conn->curTuple == NULL)
|
||||||
@ -1301,40 +1043,40 @@ getAnotherTuple(PGconn *conn, int binary)
|
|||||||
}
|
}
|
||||||
tup = conn->curTuple;
|
tup = conn->curTuple;
|
||||||
|
|
||||||
/* Get the null-value bitmap */
|
/* Get the field count and make sure it's what we expect */
|
||||||
nbytes = (nfields + BYTELEN - 1) / BYTELEN;
|
if (pqGetInt(&tupnfields, 2, conn))
|
||||||
/* malloc() only for unusually large field counts... */
|
return EOF;
|
||||||
if (nbytes > sizeof(std_bitmap))
|
|
||||||
bitmap = (char *) malloc(nbytes);
|
|
||||||
|
|
||||||
if (pqGetnchar(bitmap, nbytes, conn))
|
if (tupnfields != nfields)
|
||||||
goto EOFexit;
|
{
|
||||||
|
/* 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 */
|
/* Scan the fields */
|
||||||
bitmap_index = 0;
|
|
||||||
bmap = bitmap[bitmap_index];
|
|
||||||
bitcnt = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < nfields; i++)
|
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].value = result->null_field;
|
||||||
tup[i].len = NULL_LEN;
|
tup[i].len = NULL_LEN;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/* 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)
|
if (vlen < 0)
|
||||||
vlen = 0;
|
vlen = 0;
|
||||||
if (tup[i].value == NULL)
|
if (tup[i].value == NULL)
|
||||||
{
|
{
|
||||||
tup[i].value = (char *) pqResultAlloc(result, vlen + 1, (bool) binary);
|
tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
|
||||||
if (tup[i].value == NULL)
|
if (tup[i].value == NULL)
|
||||||
goto outOfMemory;
|
goto outOfMemory;
|
||||||
}
|
}
|
||||||
@ -1342,21 +1084,10 @@ getAnotherTuple(PGconn *conn, int binary)
|
|||||||
/* read in the value */
|
/* read in the value */
|
||||||
if (vlen > 0)
|
if (vlen > 0)
|
||||||
if (pqGetnchar((char *) (tup[i].value), vlen, conn))
|
if (pqGetnchar((char *) (tup[i].value), vlen, conn))
|
||||||
goto EOFexit;
|
return EOF;
|
||||||
/* we have to terminate this ourselves */
|
/* we have to terminate this ourselves */
|
||||||
tup[i].value[vlen] = '\0';
|
tup[i].value[vlen] = '\0';
|
||||||
}
|
}
|
||||||
/* advance the bitmap stuff */
|
|
||||||
bitcnt++;
|
|
||||||
if (bitcnt == BYTELEN)
|
|
||||||
{
|
|
||||||
bitmap_index++;
|
|
||||||
bmap = bitmap[bitmap_index];
|
|
||||||
bitcnt = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
bmap <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Success! Store the completed tuple in the result */
|
/* Success! Store the completed tuple in the result */
|
||||||
if (!addTuple(result, tup))
|
if (!addTuple(result, tup))
|
||||||
@ -1364,8 +1095,6 @@ getAnotherTuple(PGconn *conn, int binary)
|
|||||||
/* and reset for a new message */
|
/* and reset for a new message */
|
||||||
conn->curTuple = NULL;
|
conn->curTuple = NULL;
|
||||||
|
|
||||||
if (bitmap != std_bitmap)
|
|
||||||
free(bitmap);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
outOfMemory:
|
outOfMemory:
|
||||||
@ -1380,13 +1109,8 @@ outOfMemory:
|
|||||||
libpq_gettext("out of memory\n"));
|
libpq_gettext("out of memory\n"));
|
||||||
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
|
conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
|
||||||
conn->asyncStatus = PGASYNC_READY;
|
conn->asyncStatus = PGASYNC_READY;
|
||||||
/* Discard the failed message --- good idea? */
|
/* Discard the failed message by pretending we read it */
|
||||||
conn->inStart = conn->inEnd;
|
return 0;
|
||||||
|
|
||||||
EOFexit:
|
|
||||||
if (bitmap != std_bitmap)
|
|
||||||
free(bitmap);
|
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2130,9 +1854,10 @@ PQfn(PGconn *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
|
if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
|
||||||
pqPuts("", conn) < 0 || /* useless string */
|
|
||||||
pqPutInt(fnid, 4, conn) < 0 || /* function id */
|
pqPutInt(fnid, 4, conn) < 0 || /* function id */
|
||||||
pqPutInt(nargs, 4, conn) < 0) /* # of args */
|
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);
|
handleSendFailure(conn);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2145,10 +1870,12 @@ PQfn(PGconn *conn,
|
|||||||
handleSendFailure(conn);
|
handleSendFailure(conn);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (args[i].len == -1)
|
||||||
|
continue; /* it's NULL */
|
||||||
|
|
||||||
if (args[i].isint)
|
if (args[i].isint)
|
||||||
{
|
{
|
||||||
if (pqPutInt(args[i].u.integer, 4, conn))
|
if (pqPutInt(args[i].u.integer, args[i].len, conn))
|
||||||
{
|
{
|
||||||
handleSendFailure(conn);
|
handleSendFailure(conn);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2164,6 +1891,12 @@ PQfn(PGconn *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */
|
||||||
|
{
|
||||||
|
handleSendFailure(conn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (pqPutMsgEnd(conn) < 0 ||
|
if (pqPutMsgEnd(conn) < 0 ||
|
||||||
pqFlush(conn))
|
pqFlush(conn))
|
||||||
{
|
{
|
||||||
@ -2204,7 +1937,7 @@ PQfn(PGconn *conn,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (msgLength > 30000 &&
|
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);
|
handleSyncLoss(conn, id, msgLength);
|
||||||
break;
|
break;
|
||||||
@ -2243,16 +1976,13 @@ PQfn(PGconn *conn,
|
|||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case 'V': /* function result */
|
case 'V': /* function result */
|
||||||
if (pqGetc(&id, conn))
|
|
||||||
continue;
|
|
||||||
if (id == 'G')
|
|
||||||
{
|
|
||||||
/* function returned nonempty value */
|
|
||||||
if (pqGetInt(actual_result_len, 4, conn))
|
if (pqGetInt(actual_result_len, 4, conn))
|
||||||
continue;
|
continue;
|
||||||
|
if (*actual_result_len != -1)
|
||||||
|
{
|
||||||
if (result_is_int)
|
if (result_is_int)
|
||||||
{
|
{
|
||||||
if (pqGetInt(result_buf, 4, conn))
|
if (pqGetInt(result_buf, *actual_result_len, conn))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2262,24 +1992,9 @@ PQfn(PGconn *conn,
|
|||||||
conn))
|
conn))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (pqGetc(&id, conn)) /* get the last '0' */
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (id == '0')
|
|
||||||
{
|
|
||||||
/* correctly finished function result message */
|
/* correctly finished function result message */
|
||||||
status = PGRES_COMMAND_OK;
|
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);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'E': /* error return */
|
case 'E': /* error return */
|
||||||
if (pqGetErrorNotice(conn, true))
|
if (pqGetErrorNotice(conn, true))
|
||||||
@ -2744,6 +2459,18 @@ PQflush(PGconn *conn)
|
|||||||
return (pqFlush(conn));
|
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
|
* PQfreeNotify - free's the memory associated with a PGnotify
|
||||||
*
|
*
|
||||||
@ -2760,3 +2487,232 @@ PQfreeNotify(PGnotify *notify)
|
|||||||
{
|
{
|
||||||
PQfreemem(notify);
|
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;
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
|
|||||||
* pqcomm.h describe what the backend knows, not what libpq knows.
|
* 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.
|
* POSTGRES backend dependent Constants.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user