Another SELECT speedup: extract OIDs of column print functions
only once per SELECT, not once per tuple. 10% here, 10% there, pretty soon you're talking about real speedups ...
This commit is contained in:
parent
36693c0525
commit
422221c90d
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.39 1999/01/24 22:50:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.40 1999/01/27 00:36:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -27,6 +27,10 @@
|
||||
#include <mb/pg_wchar.h>
|
||||
#endif
|
||||
|
||||
static void printtup_setup(DestReceiver* self, TupleDesc typeinfo);
|
||||
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self);
|
||||
static void printtup_cleanup(DestReceiver* self);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* printtup / debugtup support
|
||||
* ----------------------------------------------------------------
|
||||
@ -64,13 +68,89 @@ getTypeOutAndElem(Oid type, Oid* typOutput, Oid* typElem)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Private state for a printtup destination object
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct { /* Per-attribute information */
|
||||
Oid typoutput; /* Oid for the attribute's type output fn */
|
||||
Oid typelem; /* typelem value to pass to the output fn */
|
||||
/* more soon... */
|
||||
} PrinttupAttrInfo;
|
||||
|
||||
typedef struct {
|
||||
DestReceiver pub; /* publicly-known function pointers */
|
||||
TupleDesc attrinfo; /* The attr info we are set up for */
|
||||
int nattrs;
|
||||
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
|
||||
} DR_printtup;
|
||||
|
||||
/* ----------------
|
||||
* Initialize: create a DestReceiver for printtup
|
||||
* ----------------
|
||||
*/
|
||||
DestReceiver*
|
||||
printtup_create_DR()
|
||||
{
|
||||
DR_printtup* self = (DR_printtup*) palloc(sizeof(DR_printtup));
|
||||
|
||||
self->pub.receiveTuple = printtup;
|
||||
self->pub.setup = printtup_setup;
|
||||
self->pub.cleanup = printtup_cleanup;
|
||||
|
||||
self->attrinfo = NULL;
|
||||
self->nattrs = 0;
|
||||
self->myinfo = NULL;
|
||||
|
||||
return (DestReceiver*) self;
|
||||
}
|
||||
|
||||
static void
|
||||
printtup_setup(DestReceiver* self, TupleDesc typeinfo)
|
||||
{
|
||||
/* ----------------
|
||||
* We could set up the derived attr info at this time, but we postpone it
|
||||
* until the first call of printtup, for 3 reasons:
|
||||
* 1. We don't waste time (compared to the old way) if there are no
|
||||
* tuples at all to output.
|
||||
* 2. Checking in printtup allows us to handle the case that the tuples
|
||||
* change type midway through (although this probably can't happen in
|
||||
* the current executor).
|
||||
* 3. Right now, ExecutorRun passes a NULL for typeinfo anyway :-(
|
||||
* ----------------
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
printtup_prepare_info(DR_printtup* myState, TupleDesc typeinfo, int numAttrs)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (myState->myinfo)
|
||||
pfree(myState->myinfo); /* get rid of any old data */
|
||||
myState->myinfo = NULL;
|
||||
myState->attrinfo = typeinfo;
|
||||
myState->nattrs = numAttrs;
|
||||
if (numAttrs <= 0)
|
||||
return;
|
||||
myState->myinfo = (PrinttupAttrInfo*)
|
||||
palloc(numAttrs * sizeof(PrinttupAttrInfo));
|
||||
for (i = 0; i < numAttrs; i++)
|
||||
{
|
||||
PrinttupAttrInfo* thisState = myState->myinfo + i;
|
||||
getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
|
||||
&thisState->typoutput, &thisState->typelem);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printtup
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
static void
|
||||
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
|
||||
{
|
||||
DR_printtup *myState = (DR_printtup*) self;
|
||||
int i,
|
||||
j,
|
||||
k,
|
||||
@ -78,12 +158,15 @@ printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
char *outputstr;
|
||||
Datum attr;
|
||||
bool isnull;
|
||||
Oid typoutput,
|
||||
typelem;
|
||||
#ifdef MULTIBYTE
|
||||
unsigned char *p;
|
||||
#endif
|
||||
|
||||
/* Set or update my derived attribute info, if needed */
|
||||
if (myState->attrinfo != typeinfo ||
|
||||
myState->nattrs != tuple->t_data->t_natts)
|
||||
printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data (in ASCII style)
|
||||
* ----------------
|
||||
@ -120,10 +203,11 @@ printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
|
||||
if (isnull)
|
||||
continue;
|
||||
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
|
||||
&typoutput, &typelem))
|
||||
if (OidIsValid(myState->myinfo[i].typoutput))
|
||||
{
|
||||
outputstr = fmgr(typoutput, attr, typelem,
|
||||
outputstr = fmgr(myState->myinfo[i].typoutput,
|
||||
attr,
|
||||
myState->myinfo[i].typelem,
|
||||
typeinfo->attrs[i]->atttypmod);
|
||||
#ifdef MULTIBYTE
|
||||
p = pg_server_to_client(outputstr, strlen(outputstr));
|
||||
@ -147,6 +231,19 @@ printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printtup_cleanup
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
printtup_cleanup(DestReceiver* self)
|
||||
{
|
||||
DR_printtup* myState = (DR_printtup*) self;
|
||||
if (myState->myinfo)
|
||||
pfree(myState->myinfo);
|
||||
pfree(myState);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printatt
|
||||
* ----------------
|
||||
@ -190,7 +287,7 @@ showatts(char *name, TupleDesc tupleDesc)
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
debugtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
|
||||
{
|
||||
int i;
|
||||
Datum attr;
|
||||
@ -221,11 +318,12 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
* We use a different data prefix, e.g. 'B' instead of 'D' to
|
||||
* indicate a tuple in internal (binary) form.
|
||||
*
|
||||
* This is same as printtup, except we don't use the typout func.
|
||||
* This is same as printtup, except we don't use the typout func,
|
||||
* and therefore have no need for persistent state.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
|
||||
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
|
||||
{
|
||||
int i,
|
||||
j,
|
||||
|
@ -26,7 +26,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.63 1999/01/25 12:01:03 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.64 1999/01/27 00:36:20 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -66,9 +66,10 @@ static void EndPlan(Plan *plan, EState *estate);
|
||||
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
|
||||
Query *parseTree, CmdType operation,
|
||||
int numberTuples, ScanDirection direction,
|
||||
void (*printfunc) ());
|
||||
static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc) (),
|
||||
EState *estate);
|
||||
DestReceiver *destfunc);
|
||||
static void ExecRetrieve(TupleTableSlot *slot,
|
||||
DestReceiver *destfunc,
|
||||
EState *estate);
|
||||
static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
EState *estate);
|
||||
static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
@ -171,7 +172,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
Plan *plan;
|
||||
TupleTableSlot *result;
|
||||
CommandDest dest;
|
||||
void (*destination) ();
|
||||
DestReceiver *destfunc;
|
||||
|
||||
/******************
|
||||
* sanity checks
|
||||
@ -188,10 +189,19 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
parseTree = queryDesc->parsetree;
|
||||
plan = queryDesc->plantree;
|
||||
dest = queryDesc->dest;
|
||||
destination = (void (*) ()) DestToFunction(dest);
|
||||
destfunc = DestToFunction(dest);
|
||||
estate->es_processed = 0;
|
||||
estate->es_lastoid = InvalidOid;
|
||||
|
||||
/******************
|
||||
* FIXME: the dest setup function ought to be handed the tuple desc
|
||||
* for the tuples to be output, but I'm not quite sure how to get that
|
||||
* info at this point. For now, passing NULL is OK because no existing
|
||||
* dest setup function actually uses the pointer.
|
||||
******************
|
||||
*/
|
||||
(*destfunc->setup) (destfunc, (TupleDesc) NULL);
|
||||
|
||||
switch (feature)
|
||||
{
|
||||
|
||||
@ -202,7 +212,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
operation,
|
||||
ALL_TUPLES,
|
||||
ForwardScanDirection,
|
||||
destination);
|
||||
destfunc);
|
||||
break;
|
||||
case EXEC_FOR:
|
||||
result = ExecutePlan(estate,
|
||||
@ -211,7 +221,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
operation,
|
||||
count,
|
||||
ForwardScanDirection,
|
||||
destination);
|
||||
destfunc);
|
||||
break;
|
||||
|
||||
/******************
|
||||
@ -225,7 +235,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
operation,
|
||||
count,
|
||||
BackwardScanDirection,
|
||||
destination);
|
||||
destfunc);
|
||||
break;
|
||||
|
||||
/******************
|
||||
@ -240,7 +250,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
operation,
|
||||
ONE_TUPLE,
|
||||
ForwardScanDirection,
|
||||
destination);
|
||||
destfunc);
|
||||
break;
|
||||
default:
|
||||
result = NULL;
|
||||
@ -248,6 +258,8 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
|
||||
break;
|
||||
}
|
||||
|
||||
(*destfunc->cleanup) (destfunc);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -745,7 +757,7 @@ ExecutePlan(EState *estate,
|
||||
CmdType operation,
|
||||
int numberTuples,
|
||||
ScanDirection direction,
|
||||
void (*printfunc) ())
|
||||
DestReceiver* destfunc)
|
||||
{
|
||||
JunkFilter *junkfilter;
|
||||
|
||||
@ -905,7 +917,7 @@ ExecutePlan(EState *estate,
|
||||
{
|
||||
case CMD_SELECT:
|
||||
ExecRetrieve(slot, /* slot containing tuple */
|
||||
printfunc, /* print function */
|
||||
destfunc, /* destination's tuple-receiver obj */
|
||||
estate); /* */
|
||||
result = slot;
|
||||
break;
|
||||
@ -961,7 +973,7 @@ ExecutePlan(EState *estate,
|
||||
*/
|
||||
static void
|
||||
ExecRetrieve(TupleTableSlot *slot,
|
||||
void (*printfunc) (),
|
||||
DestReceiver *destfunc,
|
||||
EState *estate)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
@ -988,7 +1000,7 @@ ExecRetrieve(TupleTableSlot *slot,
|
||||
* send the tuple to the front end (or the screen)
|
||||
******************
|
||||
*/
|
||||
(*printfunc) (tuple, attrtype);
|
||||
(*destfunc->receiveTuple) (tuple, attrtype, destfunc);
|
||||
IncrRetrieved();
|
||||
(estate->es_processed)++;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* spi.c--
|
||||
* Server Programming Interface
|
||||
*
|
||||
* $Id: spi.c,v 1.30 1999/01/24 05:40:48 tgl Exp $
|
||||
* $Id: spi.c,v 1.31 1999/01/27 00:36:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,8 +32,6 @@ uint32 SPI_processed = 0;
|
||||
SPITupleTable *SPI_tuptable;
|
||||
int SPI_result;
|
||||
|
||||
void spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
QueryTreeList *qtlist;
|
||||
@ -566,7 +564,7 @@ SPI_pfree(void *pointer)
|
||||
*
|
||||
*/
|
||||
void
|
||||
spi_printtup(HeapTuple tuple, TupleDesc tupdesc)
|
||||
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver* self)
|
||||
{
|
||||
SPITupleTable *tuptable;
|
||||
MemoryContext oldcxt;
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: be-dumpdata.c,v 1.20 1999/01/24 05:40:49 tgl Exp $
|
||||
* $Id: be-dumpdata.c,v 1.21 1999/01/27 00:36:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -208,7 +208,7 @@ be_typeinit(PortalEntry *entry,
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
be_printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
be_printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
|
||||
{
|
||||
int i;
|
||||
Datum attr;
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.18 1998/09/01 04:29:10 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.19 1999/01/27 00:36:28 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -275,7 +275,7 @@ print_slot(TupleTableSlot *slot)
|
||||
return;
|
||||
}
|
||||
|
||||
debugtup(slot->val, slot->ttc_tupleDescriptor);
|
||||
debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@ -1,19 +1,20 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* dest.c--
|
||||
* support for various communication destinations - see lib/H/tcop/dest.h
|
||||
* support for various communication destinations - see include/tcop/dest.h
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.23 1998/09/01 04:32:10 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.24 1999/01/27 00:36:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* BeginCommand - prepare destination for tuples of the given type
|
||||
* DestToFunction - identify per-tuple processing routines
|
||||
* EndCommand - tell destination that no more tuples will arrive
|
||||
* NullCommand - tell dest that an empty query string was recognized
|
||||
* ReadyForQuery - tell dest that we are ready for a new query
|
||||
@ -23,6 +24,13 @@
|
||||
* tuples are returned by a query to keep the backend and the
|
||||
* "destination" portals synchronized.
|
||||
*
|
||||
* There is a second level of initialization/cleanup performed by the
|
||||
* setup/cleanup routines identified by DestToFunction. This could
|
||||
* probably be merged with the work done by BeginCommand/EndCommand,
|
||||
* but as of right now BeginCommand/EndCommand are used in a rather
|
||||
* unstructured way --- some places call Begin without End, some vice
|
||||
* versa --- so I think I'll just leave 'em alone for now. tgl 1/99.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include <string.h>
|
||||
@ -47,182 +55,43 @@
|
||||
static char CommandInfo[32] = {0};
|
||||
|
||||
/* ----------------
|
||||
* output functions
|
||||
* dummy DestReceiver functions
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
donothing(HeapTuple tuple, TupleDesc attrdesc)
|
||||
donothingReceive (HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
|
||||
{
|
||||
}
|
||||
|
||||
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
|
||||
|
||||
void (*
|
||||
DestToFunction(CommandDest dest)) (HeapTuple, TupleDesc)
|
||||
static void
|
||||
donothingSetup (DestReceiver* self, TupleDesc typeinfo)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
return printtup_internal;
|
||||
break;
|
||||
}
|
||||
|
||||
case Remote:
|
||||
return printtup;
|
||||
break;
|
||||
|
||||
case Local:
|
||||
return be_printtup;
|
||||
break;
|
||||
|
||||
case Debug:
|
||||
return debugtup;
|
||||
break;
|
||||
|
||||
case SPI:
|
||||
return spi_printtup;
|
||||
break;
|
||||
|
||||
case None:
|
||||
default:
|
||||
return donothing;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* never gets here, but DECstation lint appears to be stupid...
|
||||
*/
|
||||
|
||||
return donothing;
|
||||
static void
|
||||
donothingCleanup (DestReceiver* self)
|
||||
{
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* EndCommand - tell destination that no more tuples will arrive
|
||||
* static DestReceiver structs for dest types needing no local state
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
EndCommand(char *commandTag, CommandDest dest)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
/* ----------------
|
||||
* tell the fe that the query is over
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("C", 1);
|
||||
sprintf(buf, "%s%s", commandTag, CommandInfo);
|
||||
CommandInfo[0] = 0;
|
||||
pq_putstr(buf);
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* These are necessary to sync communications between fe/be processes doing
|
||||
* COPY rel TO stdout
|
||||
*
|
||||
* or
|
||||
*
|
||||
* COPY rel FROM stdin
|
||||
*
|
||||
* NOTE: the message code letters are changed at protocol version 2.0
|
||||
* to eliminate possible confusion with data tuple messages.
|
||||
*/
|
||||
void
|
||||
SendCopyBegin(void)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("H", 1); /* new way */
|
||||
else
|
||||
pq_putnchar("B", 1); /* old way */
|
||||
}
|
||||
|
||||
void
|
||||
ReceiveCopyBegin(void)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("G", 1); /* new way */
|
||||
else
|
||||
pq_putnchar("D", 1); /* old way */
|
||||
/* We *must* flush here to ensure FE knows it can send. */
|
||||
pq_flush();
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* NullCommand - tell dest that an empty query string was recognized
|
||||
*
|
||||
* In FE/BE protocol version 1.0, this hack is necessary to support
|
||||
* libpq's crufty way of determining whether a multiple-command
|
||||
* query string is done. In protocol 2.0 it's probably not really
|
||||
* necessary to distinguish empty queries anymore, but we still do it
|
||||
* for backwards compatibility with 1.0.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
NullCommand(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
{
|
||||
/* ----------------
|
||||
* tell the fe that we saw an empty query string
|
||||
* ----------------
|
||||
*/
|
||||
pq_putstr("I");
|
||||
}
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* ReadyForQuery - tell dest that we are ready for a new query
|
||||
*
|
||||
* The ReadyForQuery message is sent in protocol versions 2.0 and up
|
||||
* so that the FE can tell when we are done processing a query string.
|
||||
*
|
||||
* Note that by flushing the stdio buffer here, we can avoid doing it
|
||||
* most other places and thus reduce the number of separate packets sent.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
ReadyForQuery(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("Z", 1);
|
||||
/* Flush output at end of cycle in any case. */
|
||||
pq_flush();
|
||||
}
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
static DestReceiver donothingDR = {
|
||||
donothingReceive, donothingSetup, donothingCleanup
|
||||
};
|
||||
static DestReceiver printtup_internalDR = {
|
||||
printtup_internal, donothingSetup, donothingCleanup
|
||||
};
|
||||
static DestReceiver be_printtupDR = {
|
||||
be_printtup, donothingSetup, donothingCleanup
|
||||
};
|
||||
static DestReceiver debugtupDR = {
|
||||
debugtup, donothingSetup, donothingCleanup
|
||||
};
|
||||
static DestReceiver spi_printtupDR = {
|
||||
spi_printtup, donothingSetup, donothingCleanup
|
||||
};
|
||||
|
||||
/* ----------------
|
||||
* BeginCommand - prepare destination for tuples of the given type
|
||||
@ -245,16 +114,16 @@ BeginCommand(char *pname,
|
||||
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
case RemoteInternal:
|
||||
/* ----------------
|
||||
* if this is a "retrieve portal" query, just return
|
||||
* if this is a "retrieve portal" query, done
|
||||
* because nothing needs to be sent to the fe.
|
||||
* ----------------
|
||||
*/
|
||||
CommandInfo[0] = 0;
|
||||
CommandInfo[0] = '\0';
|
||||
if (isIntoPortal)
|
||||
return;
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* if portal name not specified for remote query,
|
||||
@ -336,12 +205,179 @@ BeginCommand(char *pname,
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* DestToFunction - return appropriate receiver function set for dest
|
||||
* ----------------
|
||||
*/
|
||||
DestReceiver*
|
||||
DestToFunction(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case Remote:
|
||||
/* printtup wants a dynamically allocated DestReceiver */
|
||||
return printtup_create_DR();
|
||||
break;
|
||||
|
||||
case RemoteInternal:
|
||||
return & printtup_internalDR;
|
||||
break;
|
||||
|
||||
case Local:
|
||||
return & be_printtupDR;
|
||||
break;
|
||||
|
||||
case Debug:
|
||||
return & debugtupDR;
|
||||
break;
|
||||
|
||||
case SPI:
|
||||
return & spi_printtupDR;
|
||||
break;
|
||||
|
||||
case None:
|
||||
default:
|
||||
return & donothingDR;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* never gets here, but DECstation lint appears to be stupid...
|
||||
*/
|
||||
|
||||
return & donothingDR;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* EndCommand - tell destination that no more tuples will arrive
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
EndCommand(char *commandTag, CommandDest dest)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
switch (dest)
|
||||
{
|
||||
case Remote:
|
||||
case RemoteInternal:
|
||||
/* ----------------
|
||||
* tell the fe that the query is over
|
||||
* ----------------
|
||||
*/
|
||||
sprintf(buf, "C%s%s", commandTag, CommandInfo);
|
||||
pq_putstr(buf);
|
||||
CommandInfo[0] = '\0';
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* These are necessary to sync communications between fe/be processes doing
|
||||
* COPY rel TO stdout
|
||||
*
|
||||
* or
|
||||
*
|
||||
* COPY rel FROM stdin
|
||||
*
|
||||
* NOTE: the message code letters are changed at protocol version 2.0
|
||||
* to eliminate possible confusion with data tuple messages.
|
||||
*/
|
||||
void
|
||||
SendCopyBegin(void)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("H", 1); /* new way */
|
||||
else
|
||||
pq_putnchar("B", 1); /* old way */
|
||||
}
|
||||
|
||||
void
|
||||
ReceiveCopyBegin(void)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("G", 1); /* new way */
|
||||
else
|
||||
pq_putnchar("D", 1); /* old way */
|
||||
/* We *must* flush here to ensure FE knows it can send. */
|
||||
pq_flush();
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* NullCommand - tell dest that an empty query string was recognized
|
||||
*
|
||||
* In FE/BE protocol version 1.0, this hack is necessary to support
|
||||
* libpq's crufty way of determining whether a multiple-command
|
||||
* query string is done. In protocol 2.0 it's probably not really
|
||||
* necessary to distinguish empty queries anymore, but we still do it
|
||||
* for backwards compatibility with 1.0.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
NullCommand(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
/* ----------------
|
||||
* tell the fe that we saw an empty query string
|
||||
* ----------------
|
||||
*/
|
||||
pq_putstr("I");
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* ReadyForQuery - tell dest that we are ready for a new query
|
||||
*
|
||||
* The ReadyForQuery message is sent in protocol versions 2.0 and up
|
||||
* so that the FE can tell when we are done processing a query string.
|
||||
*
|
||||
* Note that by flushing the stdio buffer here, we can avoid doing it
|
||||
* most other places and thus reduce the number of separate packets sent.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
ReadyForQuery(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putnchar("Z", 1);
|
||||
/* Flush output at end of cycle in any case. */
|
||||
pq_flush();
|
||||
break;
|
||||
|
||||
case Local:
|
||||
case Debug:
|
||||
case None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
case CMD_INSERT:
|
||||
if (tuples > 1)
|
||||
lastoid = InvalidOid;
|
||||
sprintf(CommandInfo, " %u %u", lastoid, tuples);
|
||||
@ -351,7 +387,7 @@ UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples)
|
||||
sprintf(CommandInfo, " %u", tuples);
|
||||
break;
|
||||
default:
|
||||
CommandInfo[0] = 0;
|
||||
CommandInfo[0] = '\0';
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -6,20 +6,26 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: printtup.h,v 1.6 1999/01/24 05:40:46 tgl Exp $
|
||||
* $Id: printtup.h,v 1.7 1999/01/27 00:36:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PRINTTUP_H
|
||||
#define PRINTTUP_H
|
||||
|
||||
#include <access/htup.h>
|
||||
#include <access/tupdesc.h>
|
||||
#include <tcop/dest.h>
|
||||
|
||||
extern DestReceiver* printtup_create_DR(void);
|
||||
extern void showatts(char *name, TupleDesc attinfo);
|
||||
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver* self);
|
||||
extern void printtup_internal(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver* self);
|
||||
|
||||
/* XXX this one is really in executor/spi.c */
|
||||
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc,
|
||||
DestReceiver* self);
|
||||
|
||||
extern int getTypeOutAndElem(Oid type, Oid* typOutput, Oid* typElem);
|
||||
extern void printtup(HeapTuple tuple, TupleDesc typeinfo);
|
||||
extern void showatts(char *name, TupleDesc attinfo);
|
||||
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo);
|
||||
extern void printtup_internal(HeapTuple tuple, TupleDesc typeinfo);
|
||||
|
||||
#endif /* PRINTTUP_H */
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: libpq.h,v 1.25 1999/01/24 02:47:15 tgl Exp $
|
||||
* $Id: libpq.h,v 1.26 1999/01/27 00:36:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -18,8 +18,7 @@
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "libpq/libpq-be.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/tupdesc.h"
|
||||
#include "tcop/dest.h"
|
||||
|
||||
|
||||
/* ----------------
|
||||
@ -236,7 +235,8 @@ extern PortalEntry *be_currentportal(void);
|
||||
extern PortalEntry *be_newportal(void);
|
||||
extern void be_typeinit(PortalEntry *entry, TupleDesc attrs,
|
||||
int natts);
|
||||
extern void be_printtup(HeapTuple tuple, TupleDesc typeinfo);
|
||||
extern void be_printtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver* self);
|
||||
|
||||
|
||||
/* in be-pqexec.c */
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* dest.h--
|
||||
* Whenever the backend is submitted a query, the results
|
||||
* Whenever the backend executes a query, the results
|
||||
* have to go someplace - either to the standard output,
|
||||
* to a local portal buffer or to a remote portal buffer.
|
||||
*
|
||||
@ -23,21 +23,40 @@
|
||||
* a query internally. This is not used now but it may be
|
||||
* useful for the parallel optimiser/executor.
|
||||
*
|
||||
* dest.c defines three functions that implement destination management:
|
||||
*
|
||||
* BeginCommand: initialize the destination.
|
||||
* DestToFunction: return a pointer to a struct of destination-specific
|
||||
* receiver functions.
|
||||
* EndCommand: clean up the destination when output is complete.
|
||||
*
|
||||
* The DestReceiver object returned by DestToFunction may be a statically
|
||||
* allocated object (for destination types that require no local state)
|
||||
* or can be a palloc'd object that has DestReceiver as its first field
|
||||
* and contains additional fields (see printtup.c for an example). These
|
||||
* additional fields are then accessible to the DestReceiver functions
|
||||
* by casting the DestReceiver* pointer passed to them.
|
||||
* The palloc'd object is pfree'd by the DestReceiver's cleanup function.
|
||||
*
|
||||
* XXX FIXME: the initialization and cleanup code that currently appears
|
||||
* in-line in BeginCommand and EndCommand probably should be moved out
|
||||
* to routines associated with each destination receiver type.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: dest.h,v 1.16 1998/09/01 04:38:39 momjian Exp $
|
||||
* $Id: dest.h,v 1.17 1999/01/27 00:36:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef DEST_H
|
||||
#define DEST_H
|
||||
|
||||
#include <access/htup.h>
|
||||
#include <access/tupdesc.h>
|
||||
|
||||
/* ----------------
|
||||
* CommandDest is used to allow the results of calling
|
||||
* pg_eval() to go to the right place.
|
||||
* CommandDest is a simplistic means of identifying the desired
|
||||
* destination. Someday this will probably need to be improved.
|
||||
* ----------------
|
||||
*/
|
||||
typedef enum
|
||||
@ -51,25 +70,38 @@ typedef enum
|
||||
SPI /* results sent to SPI manager */
|
||||
} CommandDest;
|
||||
|
||||
/* ----------------
|
||||
* DestReceiver is a base type for destination-specific local state.
|
||||
* In the simplest cases, there is no state info, just the function
|
||||
* pointers that the executor must call.
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct _DestReceiver DestReceiver;
|
||||
|
||||
/* AttrInfo* replaced with TupleDesc, now that TupleDesc also has within it
|
||||
the number of attributes
|
||||
struct _DestReceiver {
|
||||
/* Called for each tuple to be output: */
|
||||
void (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver* self);
|
||||
/* Initialization and teardown: */
|
||||
void (*setup) (DestReceiver* self, TupleDesc typeinfo);
|
||||
void (*cleanup) (DestReceiver* self);
|
||||
/* Private fields might appear beyond this point... */
|
||||
};
|
||||
|
||||
typedef struct AttrInfo {
|
||||
int numAttr;
|
||||
Form_pg_attribute *attrs;
|
||||
} AttrInfo;
|
||||
*/
|
||||
/* The primary destination management functions */
|
||||
|
||||
extern void (*DestToFunction(CommandDest dest)) ();
|
||||
extern void BeginCommand(char *pname, int operation, TupleDesc attinfo,
|
||||
bool isIntoRel, bool isIntoPortal, char *tag,
|
||||
CommandDest dest);
|
||||
extern DestReceiver* DestToFunction(CommandDest dest);
|
||||
extern void EndCommand(char *commandTag, CommandDest dest);
|
||||
|
||||
/* Additional functions that go with destination management, more or less. */
|
||||
|
||||
extern void SendCopyBegin(void);
|
||||
extern void ReceiveCopyBegin(void);
|
||||
extern void NullCommand(CommandDest dest);
|
||||
extern void ReadyForQuery(CommandDest dest);
|
||||
extern void BeginCommand(char *pname, int operation, TupleDesc attinfo,
|
||||
bool isIntoRel, bool isIntoPortal, char *tag,
|
||||
CommandDest dest);
|
||||
extern void UpdateCommandInfo(int operation, Oid lastoid, uint32 tuples);
|
||||
|
||||
#endif /* DEST_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user